diff options
Diffstat (limited to 'src/gui/graphicsview')
36 files changed, 7387 insertions, 3493 deletions
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index 02d9bb1..9d232e4 100644 --- a/src/gui/graphicsview/graphicsview.pri +++ b/src/gui/graphicsview/graphicsview.pri @@ -1,46 +1,44 @@ # Qt graphicsview module - -HEADERS += \ - graphicsview/qgraphicsitem.h \ - graphicsview/qgraphicsitem_p.h \ - graphicsview/qgraphicsitemanimation.h \ - graphicsview/qgraphicsscene.h \ - graphicsview/qgraphicsscene_p.h \ - graphicsview/qgraphicsscene_bsp_p.h \ - graphicsview/qgraphicssceneevent.h \ - graphicsview/qgraphicsview_p.h \ - graphicsview/qgraphicsview.h - -SOURCES += \ - graphicsview/qgraphicsitem.cpp \ - graphicsview/qgraphicsitemanimation.cpp \ - graphicsview/qgraphicsscene.cpp \ - graphicsview/qgraphicsscene_bsp.cpp \ - graphicsview/qgraphicssceneevent.cpp \ - graphicsview/qgraphicsview.cpp - -# Widgets on the canvas - -HEADERS += \ - graphicsview/qgraphicslayout.h \ - graphicsview/qgraphicslayout_p.h \ - graphicsview/qgraphicslayoutitem.h \ - graphicsview/qgraphicslayoutitem_p.h \ - graphicsview/qgraphicslinearlayout.h \ - graphicsview/qgraphicswidget.h \ - graphicsview/qgraphicswidget_p.h \ - graphicsview/qgridlayoutengine_p.h \ - graphicsview/qgraphicsproxywidget.h \ - graphicsview/qgraphicsgridlayout.h - -SOURCES += \ - graphicsview/qgraphicslayout.cpp \ - graphicsview/qgraphicslayout_p.cpp \ - graphicsview/qgraphicslayoutitem.cpp \ - graphicsview/qgraphicslinearlayout.cpp \ - graphicsview/qgraphicswidget.cpp \ - graphicsview/qgraphicswidget_p.cpp \ - graphicsview/qgridlayoutengine.cpp \ - graphicsview/qgraphicsproxywidget.cpp \ - graphicsview/qgraphicsgridlayout.cpp - +HEADERS += graphicsview/qgraphicsgridlayout.h \ + graphicsview/qgraphicsitem.h \ + graphicsview/qgraphicsitem_p.h \ + graphicsview/qgraphicsitemanimation.h \ + graphicsview/qgraphicslayout.h \ + graphicsview/qgraphicslayout_p.h \ + graphicsview/qgraphicslayoutitem.h \ + graphicsview/qgraphicslayoutitem_p.h \ + graphicsview/qgraphicslinearlayout.h \ + graphicsview/qgraphicsproxywidget.h \ + graphicsview/qgraphicsscene.h \ + graphicsview/qgraphicsscene_bsp_p.h \ + graphicsview/qgraphicsscene_p.h \ + graphicsview/qgraphicsscenebsptreeindex_p.h \ + graphicsview/qgraphicssceneevent.h \ + graphicsview/qgraphicssceneindex_p.h \ + graphicsview/qgraphicsscenelinearindex_p.h \ + graphicsview/qgraphicstransform.h \ + graphicsview/qgraphicstransform_p.h \ + graphicsview/qgraphicsview.h \ + graphicsview/qgraphicsview_p.h \ + graphicsview/qgraphicswidget.h \ + graphicsview/qgraphicswidget_p.h \ + graphicsview/qgridlayoutengine_p.h +SOURCES += graphicsview/qgraphicsgridlayout.cpp \ + graphicsview/qgraphicsitem.cpp \ + graphicsview/qgraphicsitemanimation.cpp \ + graphicsview/qgraphicslayout.cpp \ + graphicsview/qgraphicslayout_p.cpp \ + graphicsview/qgraphicslayoutitem.cpp \ + graphicsview/qgraphicslinearlayout.cpp \ + graphicsview/qgraphicsproxywidget.cpp \ + graphicsview/qgraphicsscene.cpp \ + graphicsview/qgraphicsscene_bsp.cpp \ + graphicsview/qgraphicsscenebsptreeindex.cpp \ + graphicsview/qgraphicssceneevent.cpp \ + graphicsview/qgraphicssceneindex.cpp \ + graphicsview/qgraphicsscenelinearindex.cpp \ + graphicsview/qgraphicstransform.cpp \ + graphicsview/qgraphicsview.cpp \ + graphicsview/qgraphicswidget.cpp \ + graphicsview/qgraphicswidget_p.cpp \ + graphicsview/qgridlayoutengine.cpp diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index b91ee45..d11df26 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.cpp +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -100,7 +100,7 @@ QLayoutStyleInfo QGraphicsGridLayoutPrivate::styleInfo() const if (!wid) wid = new QWidget; QGraphicsItem *item = parentItem(); - QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : qApp->style(); + QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : QApplication::style(); return QLayoutStyleInfo(style, wid); } diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 40331fb..beaf42b 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,43 @@ \img graphicsview-parentchild.png - 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(), - 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(). + \section1 Transformation + + QGraphicsItem supports projective transformations in addition to its base + position, pos(). There are several ways to change an item's transformation. + For simple transformations, you can call either of the convenience + functions setRotation() or setScale(), or you can pass any transformation + matrix to setTransform(). For advanced transformation control you also have + the option of setting several combined transformations by calling + setTransformations(). + + 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(). + + Certain transformation operations produce a different outcome depending on + the order in which they are applied. For example, if you scale an + transform, and then rotate it, you may get a different result than if the + transform was rotated first. However, the order you set the transformation + properties on QGraphicsItem does not affect the resulting transformation; + QGraphicsItem always applies the properties in a fixed, defined order: + + \list + \o The item's base transform is applied (transform()) + \o The item's transformations list is applied in order (transformations()) + \o The item is rotated relative to its transform origin point (rotation(), transformOriginPoint()) + \o The item is scaled relative to its transform origin point (scale(), transformOriginPoint()) + \endlist + + \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 +186,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 +213,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 +303,34 @@ 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 the + exposedRect or matrix member of the QStyleOptionGraphicsItem. Implementers + of QGraphicsItem subclasses should set that flag if this data is required. + By default, the exposedRect is initialized to the item's boundingRect and + the matrix is untransformed. Enable this flag for more fine-grained values. + 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. + + \value ItemAcceptsInputMethod The item supports input methods typically + used for Asian languages. + This flag was introduced in Qt 4.6. + + \value ItemAutoDetectsFocusProxy The item will assign any child that + gains input focus as its focus proxy. See also focusProxy(). + This flag was introduced in Qt 4.6. */ /*! @@ -312,33 +369,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). @@ -501,6 +562,7 @@ #include "qgraphicsview.h" #include "qgraphicswidget.h" #include "qgraphicsproxywidget.h" +#include "qgraphicsscenebsptreeindex_p.h" #include <QtCore/qbitarray.h> #include <QtCore/qdebug.h> #include <QtCore/qpoint.h> @@ -522,21 +584,15 @@ #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 -// QRectF::intersects() returns false always if either the source or target -// rectangle's width or height are 0. This works around that problem. -static inline void _q_adjustRect(QRectF *rect) -{ - Q_ASSERT(rect); - if (!rect->width()) - rect->adjust(-0.00001, 0, 0.00001, 0); - if (!rect->height()) - rect->adjust(0, -0.00001, 0, 0.00001); -} - static inline void _q_adjustRect(QRect *rect) { Q_ASSERT(rect); @@ -559,29 +615,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. */ @@ -621,6 +654,10 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch // For root items only. This is the item that has either enabled or // disabled \a childFlag, or has been reparented. switch (int(childFlag)) { + case -2: + flag = AncestorFiltersChildEvents; + enabled = q->filtersChildEvents(); + break; case -1: flag = AncestorHandlesChildEvents; enabled = q->handlesChildEvents(); @@ -642,7 +679,8 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch // Inherit the enabled-state from our parents. if ((parent->d_ptr->ancestorFlags & flag) || (int(parent->d_ptr->flags & childFlag) == childFlag) - || (childFlag == -1 && parent->d_ptr->handlesChildEvents)) { + || (childFlag == -1 && parent->d_ptr->handlesChildEvents) + || (childFlag == -2 && parent->d_ptr->filtersDescendantEvents)) { enabled = true; ancestorFlags |= flag; } else { @@ -665,7 +703,9 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch ancestorFlags &= ~flag; // Don't process children if the item has the main flag set on itself. - if ((childFlag != -1 && int(flags & childFlag) == childFlag) || (int(childFlag) == -1 && handlesChildEvents)) + if ((childFlag != -1 && int(flags & childFlag) == childFlag) + || (int(childFlag) == -1 && handlesChildEvents) + || (int(childFlag) == -2 && filtersDescendantEvents)) return; } @@ -757,12 +797,79 @@ 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. */ -bool QGraphicsItemPrivate::itemIsUntransformable() const +void QGraphicsItemPrivate::combineTransformToParent(QTransform *x, const QTransform *viewTransform) const { - return (flags & QGraphicsItem::ItemIgnoresTransformations) - || (ancestorFlags & AncestorIgnoresTransformations); + // COMBINE + if (viewTransform && itemIsUntransformable()) { + *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. +*/ +void QGraphicsItemPrivate::combineTransformFromParent(QTransform *x, const QTransform *viewTransform) const +{ + // COMBINE + if (viewTransform && itemIsUntransformable()) { + *x = q_ptr->deviceTransform(*viewTransform); + } else { + x->translate(pos.x(), pos.y()); + if (transformData) + *x = transformData->computedFullTransform(x); + } +} + +void QGraphicsItemPrivate::updateSceneTransformFromParent() +{ + if (parent) { + Q_ASSERT(!parent->d_ptr->dirtySceneTransform); + if (parent->d_ptr->sceneTransformTranslateOnly) { + sceneTransform = QTransform::fromTranslate(parent->d_ptr->sceneTransform.dx() + pos.x(), + parent->d_ptr->sceneTransform.dy() + pos.y()); + } else { + sceneTransform = parent->d_ptr->sceneTransform; + sceneTransform.translate(pos.x(), pos.y()); + } + if (transformData) { + sceneTransform = transformData->computedFullTransform(&sceneTransform); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else { + sceneTransformTranslateOnly = parent->d_ptr->sceneTransformTranslateOnly; + } + } else if (!transformData) { + sceneTransform = QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransformTranslateOnly = 1; + } else if (transformData->onlyTransform) { + sceneTransform = transformData->transform; + if (!pos.isNull()) + sceneTransform *= QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else if (pos.isNull()) { + sceneTransform = transformData->computedFullTransform(); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else { + sceneTransform = QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransform = transformData->computedFullTransform(&sceneTransform); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } + dirtySceneTransform = 0; } /*! @@ -784,11 +891,234 @@ 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 (scene) { + // Deliver the change to the index + scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParentVariant); + } + + QGraphicsItem *lastSubFocusItem = subFocusItem; + if (subFocusItem) { + // Update the child focus chain; when reparenting an item that has a + // focus child, ensure that that focus child clears its focus child + // chain from our parents before it's reparented. + subFocusItem->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(-2)); + 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(-2)); + 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); + } + } + } + + // Resolve depth. + resolveDepth(parent ? parent->d_ptr->depth : -1); + dirtySceneTransform = 1; + + // Restore the sub focus chain. + if (lastSubFocusItem) + lastSubFocusItem->d_ptr->setSubFocus(); + + // Auto-update focus proxy. The closest parent that detects + // focus proxies is updated as the proxy gains or loses focus. + QGraphicsItem *p = newParent; + while (p) { + if (p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) { + p->setFocusProxy(q); + break; + } + p = p->d_ptr->parent; + } + + // 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(); + matrix *= *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(); @@ -856,27 +1186,29 @@ 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); + if (d_ptr->transformData) { + for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) { + QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i); + static_cast<QGraphicsTransformPrivate *>(t->d_ptr)->item = 0; + delete t; + } + } + delete d_ptr->transformData; delete d_ptr; qt_dataStore()->data.remove(this); @@ -934,7 +1266,7 @@ void QGraphicsItem::setGroup(QGraphicsItemGroup *group) Returns a pointer to this item's parent item. If this item does not have a parent, 0 is returned. - \sa setParentItem(), children() + \sa setParentItem(), childItems() */ QGraphicsItem *QGraphicsItem::parentItem() const { @@ -957,6 +1289,20 @@ QGraphicsItem *QGraphicsItem::topLevelItem() const } /*! + \since 4.6 + + 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 @@ -1006,6 +1352,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. @@ -1014,102 +1382,11 @@ QGraphicsWidget *QGraphicsItem::window() const the parent. You should not \l{QGraphicsScene::addItem()}{add} the item to the scene yourself. - \sa parentItem(), children() + \sa parentItem(), childItems() */ 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); } /*! @@ -1214,7 +1491,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() */ @@ -1226,12 +1505,14 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) flags = GraphicsItemFlags(itemChange(ItemFlagsChange, quint32(flags)).toUInt()); if (quint32(d_ptr->flags) == quint32(flags)) return; + if (d_ptr->scene) + d_ptr->scene->d_func()->index->itemChange(this, ItemFlagsChange, quint32(flags)); // Flags that alter the geometry of the item (or its children). 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(); @@ -1239,11 +1520,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. @@ -1271,8 +1547,25 @@ 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 ((flags & ItemAcceptsInputMethod) != (oldFlags & ItemAcceptsInputMethod)) { + // Update input method sensitivity in any views. + if (d_ptr->scene) + d_ptr->scene->d_func()->updateInputMethodSensitivityInViews(); + } + + if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath*/true); + } // Notify change. itemChange(ItemFlagsHaveChanged, quint32(flags)); @@ -1335,12 +1628,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; @@ -1418,7 +1705,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()))) { @@ -1542,7 +1831,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. @@ -1571,6 +1865,8 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo if (q_ptr->isSelected()) q_ptr->setSelected(false); } else { + geometryChanged = 1; + paintedViewBoundingRectsNeedRepaint = 1; if (isWidget && scene) { QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); if (widget->windowType() == Qt::Popup) @@ -1595,6 +1891,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(); } /*! @@ -1705,8 +2004,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) @@ -1715,6 +2014,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(); } /*! @@ -1807,9 +2109,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; @@ -1845,12 +2146,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; } /*! @@ -1866,11 +2162,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(); } /*! @@ -1905,30 +2197,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(); } /*! @@ -2041,10 +2327,10 @@ bool QGraphicsItem::acceptsHoverEvents() const stays "hovered" until the cursor leaves its area, including its children's areas. - If a parent item handles child events (setHandlesChildEvents()), it will - receive hover move, drag move, and drop events as the cursor passes - through its children, but it does not receive hover enter and hover leave, - nor drag enter and drag leave events on behalf of its children. + If a parent item handles child events, it will receive hover move, + drag move, and drop events as the cursor passes through its + children, but it does not receive hover enter and hover leave, nor + drag enter and drag leave events on behalf of its children. A QGraphicsWidget with window decorations will accept hover events regardless of the value of acceptHoverEvents(). @@ -2054,7 +2340,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(); + } } /*! @@ -2064,10 +2356,78 @@ void QGraphicsItem::setAcceptHoverEvents(bool enabled) */ void QGraphicsItem::setAcceptsHoverEvents(bool enabled) { - d_ptr->acceptsHover = quint32(enabled); + setAcceptHoverEvents(enabled); +} + +/*! \since 4.6 + + Returns true if an item accepts \l{QTouchEvent}{touch events}; + otherwise, returns false. By default, items do not accept touch events. + + \sa setAcceptTouchEvents() +*/ +bool QGraphicsItem::acceptTouchEvents() const +{ + return d_ptr->acceptTouchEvents; +} + +/*! + \since 4.6 + + If \a enabled is true, this item will accept \l{QTouchEvent}{touch events}; + otherwise, it will ignore them. By default, items do not accept + touch events. +*/ +void QGraphicsItem::setAcceptTouchEvents(bool enabled) +{ + if (d_ptr->acceptTouchEvents == quint32(enabled)) + return; + d_ptr->acceptTouchEvents = quint32(enabled); + if (d_ptr->acceptTouchEvents && d_ptr->scene && d_ptr->scene->d_func()->allItemsIgnoreTouchEvents) { + d_ptr->scene->d_func()->allItemsIgnoreTouchEvents = false; + d_ptr->scene->d_func()->enableTouchEventsOnViews(); + } +} + +/*! + \since 4.6 + + Returns true if this item filters child events (i.e., all events + intended for any of its children are instead sent to this item); + otherwise, false is returned. + + The default value is false; child events are not filtered. + + \sa setFiltersChildEvents() +*/ +bool QGraphicsItem::filtersChildEvents() const +{ + return d_ptr->filtersDescendantEvents; +} + +/*! + \since 4.6 + + If \a enabled is true, this item is set to filter all events for + all its children (i.e., all events intented for any of its + children are instead sent to this item); otherwise, if \a enabled + is false, this item will only handle its own events. The default + value is false. + + \sa filtersChildEvents() +*/ +void QGraphicsItem::setFiltersChildEvents(bool enabled) +{ + if (d_ptr->filtersDescendantEvents == enabled) + return; + + d_ptr->filtersDescendantEvents = enabled; + d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-2)); } /*! + \obsolete + Returns true if this item handles child events (i.e., all events intended for any of its children are instead sent to this item); otherwise, false is returned. @@ -2088,6 +2448,8 @@ bool QGraphicsItem::handlesChildEvents() const } /*! + \obsolete + If \a enabled is true, this item is set to handle all events for all its children (i.e., all events intented for any of its children are instead sent to this item); otherwise, if \a enabled @@ -2115,73 +2477,166 @@ void QGraphicsItem::setHandlesChildEvents(bool enabled) } /*! - Returns true if this item has keyboard input focus; otherwise, returns - false. + Returns true if this item or its \l{focusProxy()}{focus proxy} has keyboard + input focus; otherwise, returns false. - \sa QGraphicsScene::focusItem(), setFocus(), QGraphicsScene::setFocusItem() + \sa focusItem(), setFocus(), QGraphicsScene::setFocusItem() */ bool QGraphicsItem::hasFocus() const { + if (d_ptr->focusProxy) + return d_ptr->focusProxy->hasFocus(); return (d_ptr->scene && d_ptr->scene->focusItem() == this); } /*! Gives keyboard input focus to this item. The \a focusReason argument will - be passed into any focus event generated by this function; it is used to - give an explanation of what caused the item to get focus. + be passed into any \l{QFocusEvent}{focus event} generated by this function; + it is used to give an explanation of what caused the item to get focus. - Only items that set the ItemIsFocusable flag can accept keyboard focus. + Only enabled items that set the ItemIsFocusable flag can accept keyboard + focus. - If this item is not visible (i.e., isVisible() returns false), not - enabled, not associated with a scene, or if it already has input focus, - this function will do nothing. + If this item is not visible, or not associated with a scene, it will not + gain immediate input focus. However, it will be registered as the preferred + focus item for its subtree of items, should it later become visible. - As a result of calling this function, this item will receive a focus in - event with \a focusReason. If another item already has focus, that item - will first receive a focus out event indicating that it has lost input - focus. + As a result of calling this function, this item will receive a + \l{focusInEvent()}{focus in event} with \a focusReason. If another item + already has focus, that item will first receive a \l{focusOutEvent()} + {focus out event} indicating that it has lost input focus. - \sa clearFocus(), hasFocus() + \sa clearFocus(), hasFocus(), focusItem(), focusProxy() */ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) { - if (!d_ptr->scene || !isEnabled() || hasFocus() || !(d_ptr->flags & ItemIsFocusable)) + // Disabled / unfocusable items cannot accept focus. + if (!isEnabled() || !(d_ptr->flags & QGraphicsItem::ItemIsFocusable)) return; - if (isVisible()) { - // Visible items immediately gain focus from scene. - d_ptr->scene->setFocusItem(this, focusReason); - } else if (d_ptr->isWidget) { - // Just set up subfocus. - static_cast<QGraphicsWidget *>(this)->d_func()->setFocusWidget(); + + // Find focus proxy. + QGraphicsItem *f = this; + while (f->d_ptr->focusProxy) + f = f->d_ptr->focusProxy; + + // Return if it already has focus. + if (d_ptr->scene && d_ptr->scene->focusItem() == f) + return; + + // Update the child focus chain. + d_ptr->setSubFocus(); + + // Update the scene's focus item. + if (d_ptr->scene) { + QGraphicsWidget *w = window(); + if (!w || w->isActiveWindow()) { + // Visible items immediately gain focus from scene. + d_ptr->scene->d_func()->setFocusItemHelper(f, focusReason); + } } } /*! Takes keyboard input focus from the item. - If it has focus, a focus out event is sent to this item to tell it that it - is about to lose the focus. + If it has focus, a \l{focusOutEvent()}{focus out event} is sent to this item + to tell it that it is about to lose the focus. Only items that set the ItemIsFocusable flag, or widgets that set an appropriate focus policy, can accept keyboard focus. - \sa setFocus(), QGraphicsWidget::focusPolicy + \sa setFocus(), hasFocus(), QGraphicsWidget::focusPolicy */ void QGraphicsItem::clearFocus() { if (!d_ptr->scene) return; - if (d_ptr->isWidget) { - // Invisible widget items with focus must explicitly clear subfocus. - static_cast<QGraphicsWidget *>(this)->d_func()->clearFocusWidget(); - } - if (d_ptr->scene->focusItem() == this) { + // Invisible items with focus must explicitly clear subfocus. + d_ptr->clearSubFocus(); + if (hasFocus()) { // If this item has the scene's input focus, clear it. d_ptr->scene->setFocusItem(0); } } /*! + \since 4.6 + + Returns this item's focus proxy, or 0 if this item has no + focus proxy. + + \sa setFocusProxy(), ItemAutoDetectsFocusProxy, setFocus(), hasFocus() +*/ +QGraphicsItem *QGraphicsItem::focusProxy() const +{ + return d_ptr->focusProxy; +} + +/*! + \since 4.6 + + Sets the item's focus proxy to \a item. + + If an item has a focus proxy, the focus proxy will receive + input focus when the item gains input focus. The item itself + will still have focus (i.e., hasFocus() will return true), + but only the focus proxy will receive the keyboard input. + + A focus proxy can itself have a focus proxy, and so on. In + such case, keyboard input will be handled by the outermost + focus proxy. + + The focus proxy \a item must belong to the same scene as + this item. + + \sa focusProxy(), ItemAutoDetectsFocusProxy, setFocus(), hasFocus() +*/ +void QGraphicsItem::setFocusProxy(QGraphicsItem *item) +{ + if (item == d_ptr->focusProxy) + return; + if (item == this) { + qWarning("QGraphicsItem::setFocusProxy: cannot assign self as focus proxy"); + return; + } + if (item) { + if (item->d_ptr->scene != d_ptr->scene) { + qWarning("QGraphicsItem::setFocusProxy: focus proxy must be in same scene"); + return; + } + for (QGraphicsItem *f = item->focusProxy(); f != 0; f = f->focusProxy()) { + if (f == this) { + qWarning("QGraphicsItem::setFocusProxy: %p is already in the focus proxy chain", item); + return; + } + } + } + + QGraphicsItem *lastFocusProxy = d_ptr->focusProxy; + d_ptr->focusProxy = item; + if (d_ptr->scene) { + if (lastFocusProxy) + d_ptr->scene->d_func()->focusProxyReverseMap.remove(lastFocusProxy, this); + if (item) + d_ptr->scene->d_func()->focusProxyReverseMap.insert(item, this); + } +} + +/*! + \since 4.6 + + If this item, a child or descendant of this item currently has input + focus, this function will return a pointer to that item. If + no descendant has input focus, 0 is returned. + + \sa hasFocus(), setFocus(), QWidget::focusWidget() +*/ +QGraphicsItem *QGraphicsItem::focusItem() const +{ + return d_ptr->subFocusItem; +} + +/*! \since 4.4 Grabs the mouse input. @@ -2316,7 +2771,7 @@ void QGraphicsItem::ungrabKeyboard() For convenience, you can also call scenePos() to determine the item's position in scene coordinates, regardless of its parent. - \sa x(), y(), setPos(), matrix(), {The Graphics View Coordinate System} + \sa x(), y(), setPos(), transform(), {The Graphics View Coordinate System} */ QPointF QGraphicsItem::pos() const { @@ -2332,6 +2787,19 @@ QPointF QGraphicsItem::pos() const */ /*! + \since 4.6 + + 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(). @@ -2340,6 +2808,19 @@ QPointF QGraphicsItem::pos() const */ /*! + \since 4.6 + + 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). @@ -2353,35 +2834,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; } /*! @@ -2396,7 +2879,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); } /*! @@ -2469,19 +2971,239 @@ 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. + + The transformation matrix is combined with the item's rotation(), scale() + and transformations() into a combined transformations for the item. + + The default transformation matrix is an identity matrix. \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 clockwise rotation, in degrees, around the Z axis. The default + value is 0 (i.e., the item is not rotated). + + The rotation is combined with the item's scale(), transform() and + transformations() to map the item's coordinate system to the parent item. + + \sa setRotation(), transformOriginPoint(), {Transformations} +*/ +qreal QGraphicsItem::rotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->rotation; +} + +/*! + \since 4.6 + + Sets the clockwise rotation \a angle, in degrees, around the Z axis. The + default value is 0 (i.e., the item is not rotated). Assigning a negative + value will rotate the item counter-clockwise. Normally the rotation angle + is in the range (-360, 360), but it's also possible to assign values + outside of this range (e.g., a rotation of 370 degrees is the same as a + rotation of 10 degrees). + + The item is rotated around its transform origin point, which by default + is (0, 0). You can select a different transformation origin by calling + setTransformOriginPoint(). + + The rotation is combined with the item's scale(), transform() and + transformations() to map the item's coordinate system to the parent item. + + \sa rotation(), setTransformOriginPoint(), {Transformations} +*/ +void QGraphicsItem::setRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->rotation = angle; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; + + if (d_ptr->isObject) + emit static_cast<QGraphicsObject *>(this)->rotationChanged(); +} + +/*! + \since 4.6 + + Returns the scale factor of the item. The default scale factor is 1.0 + (i.e., the item is not scaled). + + The scale is combined with the item's rotation(), transform() and + transformations() to map the item's coordinate system to the parent item. + + \sa setScale(), rotation(), {Transformations} +*/ +qreal QGraphicsItem::scale() const +{ + if (!d_ptr->transformData) + return 1.; + return d_ptr->transformData->scale; +} + +/*! + \since 4.6 + + Sets the scale \a factor of the item. The default scale factor is 1.0 + (i.e., the item is not scaled). A scale factor of 0.0 will collapse the + item to a single point. If you provide a negative scale factor, the + item will be flipped and mirrored (i.e., rotated 180 degrees). + + The item is scaled around its transform origin point, which by default + is (0, 0). You can select a different transformation origin by calling + setTransformOriginPoint(). + + The scale is combined with the item's rotation(), transform() and + transformations() to map the item's coordinate system to the parent item. + + \sa scale(), setTransformOriginPoint(), {Transformations} +*/ +void QGraphicsItem::setScale(qreal factor) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->scale = factor; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; + + if (d_ptr->isObject) + emit static_cast<QGraphicsObject *>(this)->scaleChanged(); +} + + +/*! + \since 4.6 + + Returns a list of graphics transforms that currently apply to this item. + + QGraphicsTransform is for applying and controlling a chain of individual + transformation operations on an item. It's particularily useful in + animations, where each transform operation needs to be interpolated + independently, or differently. + + The transformations are combined with the item's rotation(), scale() and + transform() to map the item's coordinate system to the parent item. + + \sa scale(), rotation(), transformOriginPoint(), {Transformations} +*/ +QList<QGraphicsTransform *> QGraphicsItem::transformations() const +{ + if (!d_ptr->transformData) + return QList<QGraphicsTransform *>(); + return d_ptr->transformData->graphicsTransforms; +} + +/*! + \since 4.6 + + Sets a list of graphics \a transformations (QGraphicsTransform) that + currently apply to this item. + + If all you want is to rotate or scale an item, you should call setRotation() + or setScale() instead. If you want to set an arbitrary transformation on + an item, you can call setTransform(). + + QGraphicsTransform is for applying and controlling a chain of individual + transformation operations on an item. It's particularily useful in + animations, where each transform operation needs to be interpolated + independently, or differently. + + The transformations are combined with the item's rotation(), scale() and + transform() to map the item's coordinate system to the parent item. + + \sa scale(), setTransformOriginPoint(), {Transformations} +*/ +void QGraphicsItem::setTransformations(const QList<QGraphicsTransform *> &transformations) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->graphicsTransforms = transformations; + for (int i = 0; i < transformations.size(); ++i) + transformations.at(i)->d_func()->setItem(this); + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::appendGraphicsTransform(QGraphicsTransform *t) +{ + if (!transformData) + transformData = new QGraphicsItemPrivate::TransformData; + if (!transformData->graphicsTransforms.contains(t)) + transformData->graphicsTransforms.append(t); + + Q_Q(QGraphicsItem); + t->d_func()->setItem(q); + transformData->onlyTransform = false; + dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the origin point for the transformation in item coordinates. + + The default is QPointF(0,0). + + \sa setTransformOriginPoint(), {Transformations} +*/ +QPointF QGraphicsItem::transformOriginPoint() 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 point for the transformation in item coordinates. + + \sa transformOriginPoint(), {Transformations} +*/ +void QGraphicsItem::setTransformOriginPoint(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::setTransformOriginPoint(qreal x, qreal y) + + \since 4.6 + \overload + + Sets the origin point for the transformation in item coordinates. + This is equivalent to calling setTransformOriginPoint(QPointF(\a x, \a y)). + + \sa setTransformOriginPoint(), {Transformations} +*/ + + +/*! \obsolete Use sceneTransform() instead. @@ -2490,7 +3212,8 @@ QTransform QGraphicsItem::transform() const */ QMatrix QGraphicsItem::sceneMatrix() const { - return sceneTransform().toAffine(); + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform.toAffine(); } @@ -2507,51 +3230,14 @@ 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; - } - } - return m; + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform; } /*! @@ -2580,8 +3266,10 @@ QTransform QGraphicsItem::sceneTransform() const QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) const { // Ensure we return the standard transform if we're not untransformable. - if (!d_ptr->itemIsUntransformable()) - return sceneTransform() * viewportTransform; + if (!d_ptr->itemIsUntransformable()) { + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform * viewportTransform; + } // Find the topmost item that ignores view transformations. const QGraphicsItem *untransformedAncestor = this; @@ -2600,18 +3288,18 @@ QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) c } // First translate the base untransformable item. - QPointF mappedPoint = (untransformedAncestor->sceneTransform() * viewportTransform).map(QPointF(0, 0)); - QTransform matrix; - matrix.translate(mappedPoint.x(), mappedPoint.y()); - matrix = untransformedAncestor->transform() * matrix; + untransformedAncestor->d_ptr->ensureSceneTransform(); + QPointF mappedPoint = (untransformedAncestor->d_ptr->sceneTransform * viewportTransform).map(QPointF(0, 0)); + + // COMBINE + QTransform matrix = QTransform::fromTranslate(mappedPoint.x(), mappedPoint.y()); + 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; @@ -2654,68 +3342,59 @@ 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); } // Find the closest common ancestor. If the two items don't share an // ancestor, then the only way is to combine their scene transforms. const QGraphicsItem *commonAncestor = commonAncestorItem(other); - if (!commonAncestor) - return sceneTransform() * other->sceneTransform().inverted(ok); + if (!commonAncestor) { + d_ptr->ensureSceneTransform(); + other->d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform * other->d_ptr->sceneTransform.inverted(ok); + } // If the two items are cousins (in sibling branches), map both to the // common ancestor, and combine the two transforms. bool cousins = other != commonAncestor && this != commonAncestor; if (cousins) { bool good = false; - QTransform thisToScene; - QTransform otherToScene; - thisToScene = itemTransform(commonAncestor, &good); + QTransform thisToScene = itemTransform(commonAncestor, &good); + QTransform otherToScene(Qt::Uninitialized); if (good) otherToScene = other->itemTransform(commonAncestor, &good); if (!good) { @@ -2734,11 +3413,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); @@ -2756,37 +3431,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)); } /*! @@ -2804,32 +3476,36 @@ 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} + The transformation matrix is combined with the item's rotation(), scale() + and transformations() into a combined transformation that maps the item's + coordinate system to its parent. + + \sa transform(), setRotation(), setScale(), setTransformOriginPoint(), {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); @@ -2848,8 +3524,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() */ @@ -2859,6 +3536,16 @@ void QGraphicsItem::resetTransform() } /*! + \obsolete + + Use + + \code + setRotation(rotation() + angle); + \endcode + + 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(). @@ -2875,6 +3562,16 @@ void QGraphicsItem::rotate(qreal angle) } /*! + \obsolete + + Use + + \code + setTransform(QTransform::fromScale(sx, sy), true); + \endcode + + 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(). @@ -2883,7 +3580,7 @@ void QGraphicsItem::rotate(qreal angle) \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7 - \sa setTransform(), transform(), rotate(), shear(), translate() + \sa setTransform(), transform() */ void QGraphicsItem::scale(qreal sx, qreal sy) { @@ -2891,9 +3588,19 @@ void QGraphicsItem::scale(qreal sx, qreal sy) } /*! + \obsolete + + Use + + \code + setTransform(QTransform().shear(sh, sv), true); + \endcode + + instead. + Shears the current item transformation by (\a sh, \a sv). - \sa setTransform(), transform(), rotate(), scale(), translate() + \sa setTransform(), transform() */ void QGraphicsItem::shear(qreal sh, qreal sv) { @@ -2901,13 +3608,22 @@ void QGraphicsItem::shear(qreal sh, qreal sv) } /*! + \obsolete + + Use setPos() or setTransformOriginPoint() instead. For identical + behavior, use + + \code + setTransform(QTransform::fromTranslate(dx, dy), true); + \endcode + 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) { @@ -2952,11 +3668,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 @@ -2984,16 +3700,25 @@ void QGraphicsItem::setZValue(qreal z) qreal newZ = qreal(newZVariant.toDouble()); if (newZ == d_ptr->z) return; - d_ptr->z = z; - d_ptr->fullUpdateHelper(); if (d_ptr->scene) { - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d_ptr->scene->d_func()->invalidateSortCache(); + // Z Value has changed, we have to notify the index. + d_ptr->scene->d_func()->index->itemChange(this, ItemZValueChange, newZVariant); } + 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); + itemChange(ItemZValueHasChanged, newZVariant); + + if (d_ptr->isObject) + emit static_cast<QGraphicsObject *>(this)->zChanged(); } /*! @@ -3015,14 +3740,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; } @@ -3036,7 +3759,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, @@ -3068,19 +3791,26 @@ 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)); QRectF br = boundingRect(); br.translate(offset); - return !parentItem ? br : parentItem->sceneTransform().mapRect(br); + if (!parentItem) + return br; + if (parentItem->d_ptr->hasTranslateOnlySceneTransform()) { + br.translate(parentItem->d_ptr->sceneTransform.dx(), parentItem->d_ptr->sceneTransform.dy()); + return br; + } + return parentItem->d_ptr->sceneTransform.mapRect(br); } /*! @@ -3455,7 +4185,7 @@ bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const { if (!item) return false; - return QGraphicsScenePrivate::closestItemFirst_withoutCache(item, this) + return QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(item, this) && qt_QGraphicsItem_isObscured(this, item, boundingRect()); } @@ -3540,8 +4270,7 @@ QRegion QGraphicsItem::boundingRegion(const QTransform &itemToDeviceTransform) c p.end(); // Transform QRegion back to device space - QTransform unscale; - unscale.scale(1 / granularity, 1 / granularity); + QTransform unscale = QTransform::fromScale(1 / granularity, 1 / granularity); QRegion r; QBitmap colorMask = QBitmap::fromImage(mask.createMaskFromColor(0)); foreach (const QRect &rect, QRegion( colorMask ).rects()) { @@ -3637,7 +4366,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 */ /*! @@ -3649,174 +4378,58 @@ 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) - || !scene - || (scene->d_func()->updateAll && scene->d_func()->hasSceneRect) + return !scene + || (!visible && !ignoreVisibleBit && !this->ignoreVisible) + || (!ignoreDirtyBit && fullUpdatePending) || (!ignoreClipping && (childrenClippedToShape() && isClippedAway())) - || (!ignoreOpacity && childrenCombineOpacity() && isFullyTransparent()); + || (!ignoreOpacity && !this->ignoreOpacity && childrenCombineOpacity() && isFullyTransparent()); } /*! \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) -{ - 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) +void QGraphicsItemPrivate::addChild(QGraphicsItem *child) { - 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; + child->d_ptr->siblingIndex = children.size(); + 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); + // NB! Do not use children.removeAt(child->d_ptr->siblingIndex) because + // the child is not guaranteed to be at the index after the list is sorted. + // (see ensureSortedChildren()). + child->d_ptr->siblingIndex = -1; } /*! \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)); @@ -3828,6 +4441,9 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const return c; } +/*! + \internal +*/ void QGraphicsItemPrivate::removeExtraItemCache() { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3889,17 +4505,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; } @@ -3941,6 +4553,76 @@ 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. + updateSceneTransformFromParent(); + Q_ASSERT(!dirtySceneTransform); +} + +void QGraphicsItemPrivate::ensureSceneTransform() +{ + if (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. + invalidateChildrenSceneTransform(); + } + + QGraphicsItem *that = q_func(); + ensureSceneTransformRecursive(&that); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::setSubFocus() +{ + // Update focus child chain. + QGraphicsItem *item = q_ptr; + QGraphicsItem *parent = item; + bool hidden = !visible; + do { + parent->d_func()->subFocusItem = item; + } while (!parent->isWindow() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible)); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::clearSubFocus() +{ + // Reset focus child chain. + QGraphicsItem *parent = q_ptr; + do { + if (parent->d_ptr->subFocusItem != q_ptr) + break; + parent->d_ptr->subFocusItem = 0; + } while (!parent->isWindow() && (parent = parent->d_ptr->parent)); +} + /*! \internal @@ -3983,25 +4665,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 @@ -4030,11 +4749,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; } @@ -4053,13 +4806,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); @@ -4191,7 +4938,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); } /*! @@ -4211,7 +4961,9 @@ QPointF QGraphicsItem::mapToParent(const QPointF &point) const */ QPointF QGraphicsItem::mapToScene(const QPointF &point) const { - return sceneTransform().map(point); + if (d_ptr->hasTranslateOnlySceneTransform()) + return QPointF(point.x() + d_ptr->sceneTransform.dx(), point.y() + d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(point); } /*! @@ -4256,9 +5008,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); } /*! @@ -4277,7 +5030,9 @@ QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const */ QPolygonF QGraphicsItem::mapToScene(const QRectF &rect) const { - return sceneTransform().map(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(rect); } /*! @@ -4325,8 +5080,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); } /*! @@ -4348,7 +5105,9 @@ QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const */ QRectF QGraphicsItem::mapRectToScene(const QRectF &rect) const { - return sceneTransform().mapRect(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.mapRect(rect); } /*! @@ -4397,8 +5156,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); } /*! @@ -4420,7 +5181,9 @@ QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const */ QRectF QGraphicsItem::mapRectFromScene(const QRectF &rect) const { - return sceneTransform().inverted().mapRect(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().mapRect(rect); } /*! @@ -4457,9 +5220,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); } /*! @@ -4471,7 +5235,9 @@ QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const */ QPolygonF QGraphicsItem::mapToScene(const QPolygonF &polygon) const { - return sceneTransform().map(polygon); + if (d_ptr->hasTranslateOnlySceneTransform()) + return polygon.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(polygon); } /*! @@ -4501,10 +5267,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); } /*! @@ -4516,7 +5282,9 @@ QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const */ QPainterPath QGraphicsItem::mapToScene(const QPainterPath &path) const { - return sceneTransform().map(path); + if (d_ptr->hasTranslateOnlySceneTransform()) + return path.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(path); } /*! @@ -4553,8 +5321,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; } @@ -4576,7 +5345,9 @@ QPointF QGraphicsItem::mapFromParent(const QPointF &point) const */ QPointF QGraphicsItem::mapFromScene(const QPointF &point) const { - return sceneTransform().inverted().map(point); + if (d_ptr->hasTranslateOnlySceneTransform()) + return QPointF(point.x() - d_ptr->sceneTransform.dx(), point.y() - d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(point); } /*! @@ -4621,8 +5392,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); } /*! @@ -4642,7 +5415,9 @@ QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const */ QPolygonF QGraphicsItem::mapFromScene(const QRectF &rect) const { - return sceneTransform().inverted().map(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(rect); } /*! @@ -4677,9 +5452,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); } /*! @@ -4691,7 +5467,9 @@ QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const */ QPolygonF QGraphicsItem::mapFromScene(const QPolygonF &polygon) const { - return sceneTransform().inverted().map(polygon); + if (d_ptr->hasTranslateOnlySceneTransform()) + return polygon.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(polygon); } /*! @@ -4719,9 +5497,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); } /*! @@ -4733,7 +5512,9 @@ QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const */ QPainterPath QGraphicsItem::mapFromScene(const QPainterPath &path) const { - return sceneTransform().inverted().map(path); + if (d_ptr->hasTranslateOnlySceneTransform()) + return path.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(path); } /*! @@ -5209,7 +5990,7 @@ void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event) focus in events for this item. The default implementation calls ensureVisible(). - \sa focusOutEvent(), sceneEvent() + \sa focusOutEvent(), sceneEvent(), setFocus() */ void QGraphicsItem::focusInEvent(QFocusEvent *event) { @@ -5220,7 +6001,7 @@ void QGraphicsItem::focusInEvent(QFocusEvent *event) This event handler, for event \a event, can be reimplemented to receive focus out events for this item. The default implementation does nothing. - \sa focusInEvent(), sceneEvent() + \sa focusInEvent(), sceneEvent(), setFocus() */ void QGraphicsItem::focusOutEvent(QFocusEvent *event) { @@ -5239,7 +6020,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); } /*! @@ -5267,7 +6049,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); } /*! @@ -5604,7 +6387,7 @@ void QGraphicsItem::inputMethodEvent(QInputMethodEvent *event) surrounding text and reconversions. \a query specifies which property is queried. - \sa inputMethodEvent() + \sa inputMethodEvent(), QInputMethodEvent, QInputContext */ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const { @@ -5620,6 +6403,39 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const } /*! + Returns the current input method hints of this item. + + Input method hints are only relevant for input items. + The hints are used by the input method to indicate how it should operate. + For example, if the Qt::ImhNumbersOnly flag is set, the input method may change + its visual components to reflect that only numbers can be entered. + + The effect may vary between input method implementations. + + \since 4.6 + + \sa setInputMethodHints(), inputMethodQuery(), QInputContext +*/ +Qt::InputMethodHints QGraphicsItem::inputMethodHints() const +{ + Q_D(const QGraphicsItem); + return d->imHints; +} + +/*! + Sets the current input method hints of this item to \a hints. + + \since 4.6 + + \sa inputMethodHints(), inputMethodQuery(), QInputContext +*/ +void QGraphicsItem::setInputMethodHints(Qt::InputMethodHints hints) +{ + Q_D(QGraphicsItem); + d->imHints = hints; +} + +/*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this function, your can react to a change, and in some cases, (depending on \a @@ -5696,8 +6512,7 @@ void QGraphicsItem::addToIndex() return; } if (d_ptr->scene) - d_ptr->scene->d_func()->addToIndex(this); - d_ptr->updateHelper(); + d_ptr->scene->d_func()->index->addItem(this); } /*! @@ -5713,9 +6528,8 @@ 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); + d_ptr->scene->d_func()->index->removeItem(this); } /*! @@ -5734,11 +6548,35 @@ void QGraphicsItem::removeFromIndex() void QGraphicsItem::prepareGeometryChange() { if (d_ptr->scene) { - d_ptr->updateHelper(QRectF(), false, /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper); + d_ptr->scene->d_func()->dirtyGrowingItemsBoundingRect = true; + d_ptr->geometryChanged = 1; + d_ptr->paintedViewBoundingRectsNeedRepaint = 1; + QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func(); - scenePrivate->removeFromIndex(this); + scenePrivate->index->prepareBoundingRectChange(this); + 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[0] & scenePrivate->changedSignalMask) + || scenePrivate->views.isEmpty()) { + if (d_ptr->hasTranslateOnlySceneTransform()) { + d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), + d_ptr->sceneTransform.dy())); + } else { + d_ptr->scene->update(d_ptr->sceneTransform.mapRect(boundingRect())); + } + } } + QGraphicsItem *parent = this; + while ((parent = parent->d_ptr->parent)) + parent->d_ptr->dirtyChildrenBoundingRect = 1; + if (d_ptr->inSetPosHelper) return; @@ -5760,7 +6598,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()); @@ -5810,6 +6648,226 @@ 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::rotation + This property holds the rotation of the item in degrees. + + This specifies how many degrees to rotate the item around its transformOrigin. + The default rotation is 0 degrees (i.e. not rotated at all). +*/ + +/*! + \fn QGraphicsObject::rotationChanged() + + This signal gets emitted whenever the roation of the item changes. +*/ + +/*! + \property QGraphicsObject::scale + This property holds the scale of the item. + + A scale of less than 1 means the item will be displayed smaller than + normal, and a scale of greater than 1 means the item will be + displayed larger than normal. A negative scale means the item will + be mirrored. + + By default, items are displayed at a scale of 1 (i.e. at their + normal size). + + Scaling is from the item's transformOrigin. +*/ + +/*! + \fn void QGraphicsObject::scaleChanged() + + This signal is emitted when the scale of the item changes. +*/ + + +/*! + \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 +*/ + +/*! + \fn const QObjectList &QGraphicsObject::children() const + \internal + + This function returns the same value as QObject::children(). It's + provided to differentiate between the obsolete member + QGraphicsItem::children() and QObject::children(). QGraphicsItem now + provides childItems() instead. +*/ + +/*! + \property QGraphicsObject::transformOriginPoint + \brief the transformation origin + + This property sets a specific point in the items coordiante system as the + origin for scale and rotation. + + \sa scale, rotation, QGraphicsItem::transformOriginPoint() +*/ + + +/*! \class QAbstractGraphicsShapeItem \brief The QAbstractGraphicsShapeItem class provides a common base for all path items. @@ -7399,7 +8457,6 @@ void QGraphicsPixmapItem::setTransformationMode(Qt::TransformationMode mode) { Q_D(QGraphicsPixmapItem); if (mode != d->transformationMode) { - d_ptr->updateHelper(); d->transformationMode = mode; update(); } @@ -7487,9 +8544,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); @@ -7636,6 +8691,7 @@ public: QGraphicsTextItem *qq; }; + /*! Constructs a QGraphicsTextItem, using \a text as the default plain text. \a parent is passed to QGraphicsItem's constructor. @@ -7648,13 +8704,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); } /*! @@ -7669,11 +8726,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); } /*! @@ -7944,19 +9002,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); @@ -7981,14 +9039,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); @@ -8278,9 +9336,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. @@ -8690,13 +9748,10 @@ QVariant QGraphicsSimpleTextItem::extension(const QVariant &variant) const setParentItem(). The boundingRect() function of QGraphicsItemGroup returns the - bounding rectangle of all items in the item group. In addition, - item groups have handlesChildEvents() enabled by default, so all - events sent to a member of the group go to the item group (i.e., - selecting one item in a group will select them all). - QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on its - children (i.e., with respect to the geometry of the group item, the - children are treated as if they were transformable). + bounding rectangle of all items in the item group. + QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on + its children (i.e., with respect to the geometry of the group + item, the children are treated as if they were transformable). There are two ways to construct an item group. The easiest and most common approach is to pass a list of items (e.g., all @@ -8781,6 +9836,8 @@ void QGraphicsItemGroup::addToGroup(QGraphicsItem *item) return; } + // COMBINE + // ### Use itemTransform() instead. QTransform oldSceneMatrix = item->sceneTransform(); item->setPos(mapFromItem(item, 0, 0)); item->setParentItem(this); @@ -8884,17 +9941,11 @@ QDebug operator<<(QDebug debug, QGraphicsItem *item) return debug; } - QStringList flags; - if (item->isVisible()) flags << QLatin1String("isVisible"); - if (item->isEnabled()) flags << QLatin1String("isEnabled"); - if (item->isSelected()) flags << QLatin1String("isSelected"); - if (item->hasFocus()) flags << QLatin1String("HasFocus"); - debug << "QGraphicsItem(this =" << ((void*)item) << ", parent =" << ((void*)item->parentItem()) << ", pos =" << item->pos() - << ", z =" << item->zValue() << ", flags = {" - << flags.join(QLatin1String("|")) << " })"; + << ", z =" << item->zValue() << ", flags = " + << item->flags() << ")"; return debug; } @@ -9019,6 +10070,21 @@ 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; + case QGraphicsItem::ItemAcceptsInputMethod: + str = "ItemAcceptsInputMethod"; + break; + case QGraphicsItem::ItemAutoDetectsFocusProxy: + str = "ItemAutoDetectsFocusProxy"; + break; } debug << str; return debug; @@ -9026,17 +10092,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; } diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index a6621ce..f142b0f 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -63,6 +63,7 @@ class QBrush; class QCursor; class QFocusEvent; class QGraphicsItemGroup; +class QGraphicsObject; class QGraphicsSceneContextMenuEvent; class QGraphicsSceneDragDropEvent; class QGraphicsSceneEvent; @@ -70,6 +71,7 @@ class QGraphicsSceneHoverEvent; class QGraphicsSceneMouseEvent; class QGraphicsSceneWheelEvent; class QGraphicsScene; +class QGraphicsTransform; class QGraphicsWidget; class QInputMethodEvent; class QKeyEvent; @@ -94,7 +96,13 @@ public: ItemIgnoresTransformations = 0x20, ItemIgnoresParentOpacity = 0x40, ItemDoesntPropagateOpacityToChildren = 0x80, - ItemStacksBehindParent = 0x100 + ItemStacksBehindParent = 0x100, + ItemUsesExtendedStyleOption = 0x200, + ItemHasNoContents = 0x400, + ItemSendsGeometryChanges = 0x800, + ItemAcceptsInputMethod = 0x1000, + ItemAutoDetectsFocusProxy = 0x2000 + // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) @@ -136,7 +144,7 @@ public: QGraphicsItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -146,6 +154,7 @@ public: QGraphicsItem *parentItem() const; QGraphicsItem *topLevelItem() const; + QGraphicsObject *parentObject() const; QGraphicsWidget *parentWidget() const; QGraphicsWidget *topLevelWidget() const; QGraphicsWidget *window() const; @@ -155,6 +164,9 @@ public: bool isWidget() const; bool isWindow() const; + QGraphicsObject *toGraphicsObject(); + const QGraphicsObject *toGraphicsObject() const; + QGraphicsItemGroup *group() const; void setGroup(QGraphicsItemGroup *group); @@ -199,10 +211,15 @@ public: Qt::MouseButtons acceptedMouseButtons() const; void setAcceptedMouseButtons(Qt::MouseButtons buttons); - bool acceptsHoverEvents() const; // obsolete - void setAcceptsHoverEvents(bool enabled); // obsolete + bool acceptsHoverEvents() const; // ### obsolete + void setAcceptsHoverEvents(bool enabled); // ### obsolete bool acceptHoverEvents() const; void setAcceptHoverEvents(bool enabled); + bool acceptTouchEvents() const; + void setAcceptTouchEvents(bool enabled); + + bool filtersChildEvents() const; + void setFiltersChildEvents(bool enabled); bool handlesChildEvents() const; void setHandlesChildEvents(bool enabled); @@ -211,6 +228,11 @@ public: void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason); void clearFocus(); + QGraphicsItem *focusProxy() const; + void setFocusProxy(QGraphicsItem *item); + + QGraphicsItem *focusItem() const; + void grabMouse(); void ungrabMouse(); void grabKeyboard(); @@ -219,7 +241,9 @@ public: // Positioning in scene coordinates QPointF pos() const; inline qreal x() const { return pos().x(); } + void setX(qreal x); inline qreal y() const { return pos().y(); } + void setY(qreal y); QPointF scenePos() const; void setPos(const QPointF &pos); inline void setPos(qreal x, qreal y); @@ -239,11 +263,26 @@ public: QTransform itemTransform(const QGraphicsItem *other, bool *ok = 0) const; void setTransform(const QTransform &matrix, bool combine = false); void resetTransform(); - - void rotate(qreal angle); - void scale(qreal sx, qreal sy); - void shear(qreal sh, qreal sv); - void translate(qreal dx, qreal dy); + + void rotate(qreal angle); // ### obsolete + void scale(qreal sx, qreal sy); // ### obsolete + void shear(qreal sh, qreal sv); // ### obsolete + void translate(qreal dx, qreal dy); // ### obsolete + + void setRotation(qreal angle); + qreal rotation() const; + + void setScale(qreal scale); + qreal scale() const; + + QList<QGraphicsTransform *> transformations() const; + void setTransformations(const QList<QGraphicsTransform *> &transformations); + + QPointF transformOriginPoint() const; + void setTransformOriginPoint(const QPointF &origin); + inline void setTransformOriginPoint(qreal x, qreal y) + { setTransformOriginPoint(QPointF(x,y)); } + virtual void advance(int phase); // Stacking order @@ -336,6 +375,9 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); + Qt::InputMethodHints inputMethodHints() const; + void setInputMethodHints(Qt::InputMethodHints hints); + enum { Type = 1, UserType = 65536 @@ -393,11 +435,17 @@ private: friend class QGraphicsScene; friend class QGraphicsScenePrivate; friend class QGraphicsSceneFindItemBspTreeVisitor; + friend class QGraphicsSceneBspTree; friend class QGraphicsView; friend class QGraphicsViewPrivate; friend class QGraphicsWidget; friend class QGraphicsWidgetPrivate; friend class QGraphicsProxyWidgetPrivate; + friend class QGraphicsSceneIndex; + friend class QGraphicsSceneIndexPrivate; + friend class QGraphicsSceneBspTreeIndex; + friend class QGraphicsSceneBspTreeIndexPrivate; + friend class QGraphicsTransformPrivate; friend class ::tst_QGraphicsItem; friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *); friend bool qt_closestItemFirst(const QGraphicsItem *, const QGraphicsItem *); @@ -450,13 +498,58 @@ inline QRectF QGraphicsItem::mapRectFromParent(qreal ax, qreal ay, qreal w, qrea inline QRectF QGraphicsItem::mapRectFromScene(qreal ax, qreal ay, qreal w, qreal h) const { return mapRectFromScene(QRectF(ax, ay, w, h)); } + +class Q_GUI_EXPORT QGraphicsObject : public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_PROPERTY(QGraphicsObject * parent READ parentObject WRITE setParentItem NOTIFY parentChanged DESIGNABLE false) + Q_PROPERTY(QString id READ objectName WRITE setObjectName) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) + Q_PROPERTY(QPointF pos READ pos WRITE setPos) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(qreal z READ zValue WRITE setZValue NOTIFY zChanged) + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) + Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint WRITE setTransformOriginPoint) +public: + QGraphicsObject(QGraphicsItem *parent = 0); + + // ### Qt 5: Disambiguate +#ifdef Q_NO_USING_KEYWORD + const QObjectList &children() const { return QObject::children(); } +#else + using QObject::children; +#endif + +Q_SIGNALS: + void parentChanged(); + void opacityChanged(); + void visibleChanged(); + void enabledChanged(); + void xChanged(); + void yChanged(); + void zChanged(); + void rotationChanged(); + void scaleChanged(); + +protected: + QGraphicsObject(QGraphicsItemPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene); +private: + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; +}; + + class QAbstractGraphicsShapeItemPrivate; class Q_GUI_EXPORT QAbstractGraphicsShapeItem : public QGraphicsItem { public: QAbstractGraphicsShapeItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -486,13 +579,13 @@ class Q_GUI_EXPORT QGraphicsPathItem : public QAbstractGraphicsShapeItem public: QGraphicsPathItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsPathItem(const QPainterPath &path, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -529,19 +622,19 @@ class Q_GUI_EXPORT QGraphicsRectItem : public QAbstractGraphicsShapeItem public: QGraphicsRectItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -582,19 +675,19 @@ class Q_GUI_EXPORT QGraphicsEllipseItem : public QAbstractGraphicsShapeItem public: QGraphicsEllipseItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -641,14 +734,14 @@ class Q_GUI_EXPORT QGraphicsPolygonItem : public QAbstractGraphicsShapeItem public: QGraphicsPolygonItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsPolygonItem(const QPolygonF &polygon, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -688,19 +781,19 @@ class Q_GUI_EXPORT QGraphicsLineItem : public QGraphicsItem public: QGraphicsLineItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -748,13 +841,13 @@ public: QGraphicsPixmapItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -801,7 +894,7 @@ inline void QGraphicsPixmapItem::setOffset(qreal ax, qreal ay) class QGraphicsTextItemPrivate; class QTextDocument; class QTextCursor; -class Q_GUI_EXPORT QGraphicsTextItem : public QObject, public QGraphicsItem +class Q_GUI_EXPORT QGraphicsTextItem : public QGraphicsObject { Q_OBJECT QDOC_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks) @@ -810,13 +903,13 @@ class Q_GUI_EXPORT QGraphicsTextItem : public QObject, public QGraphicsItem public: QGraphicsTextItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsTextItem(const QString &text, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -911,13 +1004,13 @@ class Q_GUI_EXPORT QGraphicsSimpleTextItem : public QAbstractGraphicsShapeItem public: QGraphicsSimpleTextItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -957,7 +1050,7 @@ class Q_GUI_EXPORT QGraphicsItemGroup : public QGraphicsItem public: QGraphicsItemGroup(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -1013,4 +1106,3 @@ QT_END_NAMESPACE QT_END_HEADER #endif // QGRAPHICSITEM_H - diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 233c674..805b554 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -54,6 +54,13 @@ // #include "qgraphicsitem.h" +#include "qset.h" +#include "qpixmapcache.h" +#include "qgraphicsview_p.h" +#include "qgraphicstransform.h" +#include "qgraphicstransform_p.h" + +#include <QtCore/qpoint.h> #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW @@ -69,13 +76,14 @@ public: // ItemCoordinateCache only QRect boundingRect; QSize fixedSize; - QString key; + QPixmapCache::Key key; // DeviceCoordinateCache only struct DeviceData { + DeviceData() {} QTransform lastTransform; QPoint cacheIndent; - QString key; + QPixmapCache::Key key; }; QMap<QPaintDevice *, DeviceData> deviceData; @@ -92,29 +100,33 @@ class Q_AUTOTEST_EXPORT QGraphicsItemPrivate Q_DECLARE_PUBLIC(QGraphicsItem) public: enum Extra { - ExtraTransform, ExtraToolTip, ExtraCursor, ExtraCacheData, ExtraMaxDeviceCoordCacheSize, - ExtraBoundingRegionGranularity, - ExtraOpacity, - ExtraEffectiveOpacity + ExtraBoundingRegionGranularity }; enum AncestorFlag { NoFlag = 0, AncestorHandlesChildEvents = 0x1, AncestorClipsChildren = 0x2, - AncestorIgnoresTransformations = 0x4 + AncestorIgnoresTransformations = 0x4, + AncestorFiltersChildEvents = 0x8 }; inline QGraphicsItemPrivate() : z(0), + opacity(1.), scene(0), parent(0), + transformData(0), index(-1), + siblingIndex(-1), depth(0), + focusProxy(0), + subFocusItem(0), + imHints(Qt::ImhNone), acceptedMouseButtons(0x1f), visible(1), explicitlyHidden(0), @@ -126,14 +138,10 @@ public: isMemberOfGroup(0), handlesChildEvents(0), itemDiscovered(0), - hasTransform(0), hasCursor(0), ancestorFlags(0), cacheMode(0), hasBoundingRegionGranularity(0), - flags(0), - hasOpacity(0), - hasEffectiveOpacity(0), isWidget(0), dirty(0), dirtyChildren(0), @@ -141,9 +149,23 @@ public: dirtyClipPath(1), emptyClipPath(0), inSetPosHelper(0), - allChildrenCombineOpacity(1), + needSortChildren(1), + allChildrenDirty(0), + fullUpdatePending(0), + flags(0), + dirtyChildrenBoundingRect(1), + paintedViewBoundingRectsNeedRepaint(0), + dirtySceneTransform(1), + geometryChanged(1), + inDestructor(0), + isObject(0), + ignoreVisible(0), + ignoreOpacity(0), + acceptTouchEvents(0), + acceptedTouchBeginEvent(0), + filtersDescendantEvents(0), + sceneTransformTranslateOnly(0), globalStackingOrder(-1), - sceneTransformIndex(-1), q_ptr(0) { } @@ -151,28 +173,48 @@ public: inline virtual ~QGraphicsItemPrivate() { } + static const QGraphicsItemPrivate *get(const QGraphicsItem *item) + { + return item->d_ptr; + } + static QGraphicsItemPrivate *get(QGraphicsItem *item) + { + return item->d_ptr; + } + void updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag, AncestorFlag flag = NoFlag, bool enabled = false, bool root = true); void setIsMemberOfGroup(bool enabled); void remapItemPos(QEvent *event, QGraphicsItem *item); QPointF genericMapFromScene(const QPointF &pos, const QWidget *viewport) const; - bool itemIsUntransformable() const; + inline bool itemIsUntransformable() const + { + return (flags & QGraphicsItem::ItemIgnoresTransformations) + || (ancestorFlags & AncestorIgnoresTransformations); + } + + void combineTransformToParent(QTransform *x, const QTransform *viewTransform = 0) const; + void combineTransformFromParent(QTransform *x, const QTransform *viewTransform = 0) const; + virtual void updateSceneTransformFromParent(); // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4. virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const; static bool movableAncestorIsSelected(const QGraphicsItem *item); void setPosHelper(const QPointF &pos); + void setTransformHelper(const QTransform &transform); + void appendGraphicsTransform(QGraphicsTransform *t); void setVisibleHelper(bool newVisible, bool explicitly, bool update = true); void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; - void updateHelper(const QRectF &rect = QRectF(), bool force = false, bool maybeDirtyClipPath = false); - void fullUpdateHelper(bool childrenOnly = false, bool maybeDirtyClipPath = false, bool ignoreOpacity = false); - void updateEffectiveOpacity(); - void resolveEffectiveOpacity(qreal effectiveParentOpacity); void resolveDepth(int parentDepth); - void invalidateSceneTransformCache(); + void addChild(QGraphicsItem *child); + void removeChild(QGraphicsItem *child); + void setParentItemHelper(QGraphicsItem *parent); + void childrenBoundingRectHelper(QTransform *x, QRectF *rect); + void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, + const QRegion &exposedRegion, bool allItems = false) const; virtual void resolveFont(uint inheritedMask) { @@ -236,8 +278,10 @@ public: bool operator<(Extra extra) const { return type < extra; } }; + QList<ExtraStruct> extras; + QGraphicsItemCache *maybeExtraItemCache() const; QGraphicsItemCache *extraItemCache() const; void removeExtraItemCache(); @@ -261,12 +305,83 @@ public: void invalidateCachedClipPathRecursively(bool childrenOnly = false, const QRectF &emptyIfOutsideThisRect = QRectF()); void updateCachedClipPathFromSetPosHelper(const QPointF &newPos); + void ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem); + void ensureSceneTransform(); + + inline bool hasTranslateOnlySceneTransform() + { + ensureSceneTransform(); + return sceneTransformTranslateOnly; + } + + inline void invalidateChildrenSceneTransform() + { + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->dirtySceneTransform = 1; + } + + inline qreal calcEffectiveOpacity() const + { + qreal o = opacity; + QGraphicsItem *p = parent; + int myFlags = flags; + while (p) { + int parentFlags = p->d_ptr->flags; + + // 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 ((myFlags & QGraphicsItem::ItemIgnoresParentOpacity) + || (parentFlags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) { + break; + } + + o *= p->d_ptr->opacity; + p = p->d_ptr->parent; + myFlags = parentFlags; + } + return o; + } inline bool isFullyTransparent() const - { return hasEffectiveOpacity && qFuzzyCompare(q_func()->effectiveOpacity() + 1, qreal(1.0)); } + { + if (opacity < 0.001) + return true; + if (!parent) + return false; + + return calcEffectiveOpacity() < 0.001; + } + + inline qreal effectiveOpacity() const { + if (!parent || !opacity) + return opacity; + + return calcEffectiveOpacity(); + } + + inline qreal combineOpacityFromParent(qreal parentOpacity) const + { + if (parent && !(flags & QGraphicsItem::ItemIgnoresParentOpacity) + && !(parent->d_ptr->flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) { + return parentOpacity * opacity; + } + return opacity; + } inline bool childrenCombineOpacity() const - { return allChildrenCombineOpacity || children.isEmpty(); } + { + if (!children.size()) + return true; + if (flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren) + return false; + + for (int i = 0; i < children.size(); ++i) { + if (children.at(i)->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity) + return false; + } + return true; + } inline bool isClippedAway() const { return !dirtyClipPath && q_func()->isClipped() && (emptyClipPath || cachedClipPath.isEmpty()); } @@ -281,16 +396,33 @@ public: || (childrenCombineOpacity() && isFullyTransparent()); } + void setSubFocus(); + void clearSubFocus(); + + inline QTransform transformToParent() const; + inline void ensureSortedChildren(); + QPainterPath cachedClipPath; + QRectF childrenBoundingRect; + QRectF needsRepaint; + QMap<QWidget *, QRect> paintedViewBoundingRects; QPointF pos; qreal z; + qreal opacity; QGraphicsScene *scene; QGraphicsItem *parent; QList<QGraphicsItem *> children; + struct TransformData; + TransformData *transformData; + QTransform sceneTransform; int index; + int siblingIndex; int depth; + QGraphicsItem *focusProxy; + QGraphicsItem *subFocusItem; + Qt::InputMethodHints imHints; - // Packed 32 bytes + // Packed 32 bits quint32 acceptedMouseButtons : 5; quint32 visible : 1; quint32 explicitlyHidden : 1; @@ -302,32 +434,122 @@ public: quint32 isMemberOfGroup : 1; quint32 handlesChildEvents : 1; quint32 itemDiscovered : 1; - quint32 hasTransform : 1; quint32 hasCursor : 1; - quint32 ancestorFlags : 3; + quint32 ancestorFlags : 4; quint32 cacheMode : 2; quint32 hasBoundingRegionGranularity : 1; - quint32 flags : 9; - - // New 32 bytes - quint32 hasOpacity : 1; - quint32 hasEffectiveOpacity : 1; quint32 isWidget : 1; - quint32 dirty : 1; - quint32 dirtyChildren : 1; + quint32 dirty : 1; + quint32 dirtyChildren : 1; quint32 localCollisionHack : 1; quint32 dirtyClipPath : 1; quint32 emptyClipPath : 1; quint32 inSetPosHelper : 1; - quint32 allChildrenCombineOpacity : 1; + quint32 needSortChildren : 1; + quint32 allChildrenDirty : 1; + + // New 32 bits + quint32 fullUpdatePending : 1; + quint32 flags : 14; + quint32 dirtyChildrenBoundingRect : 1; + quint32 paintedViewBoundingRectsNeedRepaint : 1; + quint32 dirtySceneTransform : 1; + quint32 geometryChanged : 1; + quint32 inDestructor : 1; + quint32 isObject : 1; + quint32 ignoreVisible : 1; + quint32 ignoreOpacity : 1; + quint32 acceptTouchEvents : 1; + quint32 acceptedTouchBeginEvent : 1; + quint32 filtersDescendantEvents : 1; + quint32 sceneTransformTranslateOnly : 1; + quint32 unused : 5; // feel free to use // Optional stacking order int globalStackingOrder; - int sceneTransformIndex; - QGraphicsItem *q_ptr; }; +struct QGraphicsItemPrivate::TransformData +{ + QTransform transform; + qreal scale; + qreal rotation; + qreal xOrigin; + qreal yOrigin; + QList<QGraphicsTransform *> graphicsTransforms; + bool onlyTransform; + + TransformData() : + scale(1.0), rotation(0.0), + xOrigin(0.0), yOrigin(0.0), + onlyTransform(true) + { } + + QTransform computedFullTransform(QTransform *postmultiplyTransform = 0) const + { + if (onlyTransform) { + if (!postmultiplyTransform || postmultiplyTransform->isIdentity()) + return transform; + if (transform.isIdentity()) + return *postmultiplyTransform; + return transform * *postmultiplyTransform; + } + + QTransform x(transform); + for (int i = 0; i < graphicsTransforms.size(); ++i) + graphicsTransforms.at(i)->applyTo(&x); + x.translate(xOrigin, yOrigin); + x.rotate(rotation, Qt::ZAxis); + x.scale(scale, scale); + x.translate(-xOrigin, -yOrigin); + if (postmultiplyTransform) + x *= *postmultiplyTransform; + return x; + } +}; + +/*! + \internal +*/ +inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Return true if sibling item1 is on top of item2. + const QGraphicsItemPrivate *d1 = item1->d_ptr; + const QGraphicsItemPrivate *d2 = item2->d_ptr; + bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent; + bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent; + if (f1 != f2) + return f2; + if (d1->z != d2->z) + return d1->z > d2->z; + return d1->siblingIndex > d2->siblingIndex; +} + +/*! + \internal +*/ +static inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ return qt_closestLeaf(item2, item1); } + +/* + return the full transform of the item to the parent. This include the position and all the transform data +*/ +inline QTransform QGraphicsItemPrivate::transformToParent() const +{ + QTransform matrix; + combineTransformToParent(&matrix); + return matrix; +} + +inline void QGraphicsItemPrivate::ensureSortedChildren() +{ + if (needSortChildren) { + qSort(children.begin(), children.end(), qt_notclosestLeaf); + needSortChildren = 0; + } +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslayout_p.cpp b/src/gui/graphicsview/qgraphicslayout_p.cpp index 6296207..83bf14b 100644 --- a/src/gui/graphicsview/qgraphicslayout_p.cpp +++ b/src/gui/graphicsview/qgraphicslayout_p.cpp @@ -120,8 +120,8 @@ static bool removeLayoutItemFromLayout(QGraphicsLayout *lay, QGraphicsLayoutItem if (!lay) return false; - QGraphicsLayoutItem *child; - for (int i = 0; (child = lay->itemAt(i)); ++i) { + for (int i = lay->count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *child = lay->itemAt(i); if (child && child->isLayout()) { if (removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(child), layoutItem)) return true; diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp index e4c80a5..e280162 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.cpp +++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp @@ -108,13 +108,22 @@ static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qre \internal */ QGraphicsLayoutItemPrivate::QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *par, bool layout) - : parent(par), isLayout(layout), ownedByLayout(false), graphicsItem(0) + : parent(par), userSizeHints(0), isLayout(layout), ownedByLayout(false), graphicsItem(0) { } /*! \internal */ +QGraphicsLayoutItemPrivate::~QGraphicsLayoutItemPrivate() +{ + // Remove any lazily allocated data + delete[] userSizeHints; +} + +/*! + \internal +*/ void QGraphicsLayoutItemPrivate::init() { sizeHintCacheDirty = true; @@ -132,7 +141,8 @@ QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) for (int i = 0; i < Qt::NSizeHints; ++i) { cachedSizeHints[i] = constraint; - combineSize(cachedSizeHints[i], userSizeHints[i]); + if (userSizeHints) + combineSize(cachedSizeHints[i], userSizeHints[i]); } QSizeF &minS = cachedSizeHints[Qt::MinimumSize]; @@ -198,6 +208,58 @@ QGraphicsItem *QGraphicsLayoutItemPrivate::parentItem() const } /*! + \internal + + Ensures that userSizeHints is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsLayoutItemPrivate::ensureUserSizeHints() +{ + if (!userSizeHints) + userSizeHints = new QSizeF[Qt::NSizeHints]; +} + +/*! + \internal + + Sets the user size hint \a which to \a size. Use an invalid size to unset the size hint. + */ +void QGraphicsLayoutItemPrivate::setSize(Qt::SizeHint which, const QSizeF &size) +{ + Q_Q(QGraphicsLayoutItem); + + if (userSizeHints) { + if (size == userSizeHints[which]) + return; + } else if (!size.isValid()) { + return; + } + + ensureUserSizeHints(); + userSizeHints[which] = size; + q->updateGeometry(); +} + +/*! + \internal + + Sets the width of the user size hint \a which to \a width. + */ +void QGraphicsLayoutItemPrivate::setSizeComponent( + Qt::SizeHint which, SizeComponent component, qreal value) +{ + Q_Q(QGraphicsLayoutItem); + ensureUserSizeHints(); + qreal &userValue = (component == Width) + ? userSizeHints[which].rwidth() + : userSizeHints[which].rheight(); + if (value == userValue) + return; + userValue = value; + q->updateGeometry(); +} + +/*! \class QGraphicsLayoutItem \brief The QGraphicsLayoutItem class can be inherited to allow your custom items to be managed by layouts. @@ -381,12 +443,7 @@ QSizePolicy QGraphicsLayoutItem::sizePolicy() const */ void QGraphicsLayoutItem::setMinimumSize(const QSizeF &size) { - Q_D(QGraphicsLayoutItem); - if (size == d->userSizeHints[Qt::MinimumSize]) - return; - - d->userSizeHints[Qt::MinimumSize] = size; - updateGeometry(); + d_ptr->setSize(Qt::MinimumSize, size); } /*! @@ -416,12 +473,7 @@ QSizeF QGraphicsLayoutItem::minimumSize() const */ void QGraphicsLayoutItem::setMinimumWidth(qreal width) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rwidth(); - if (width == userSizeHint) - return; - userSizeHint = width; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MinimumSize, d_ptr->Width, width); } /*! @@ -431,12 +483,7 @@ void QGraphicsLayoutItem::setMinimumWidth(qreal width) */ void QGraphicsLayoutItem::setMinimumHeight(qreal height) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rheight(); - if (height == userSizeHint) - return; - userSizeHint = height; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MinimumSize, d_ptr->Height, height); } @@ -450,12 +497,7 @@ void QGraphicsLayoutItem::setMinimumHeight(qreal height) */ void QGraphicsLayoutItem::setPreferredSize(const QSizeF &size) { - Q_D(QGraphicsLayoutItem); - if (size == d->userSizeHints[Qt::PreferredSize]) - return; - - d->userSizeHints[Qt::PreferredSize] = size; - updateGeometry(); + d_ptr->setSize(Qt::PreferredSize, size); } /*! @@ -485,12 +527,7 @@ QSizeF QGraphicsLayoutItem::preferredSize() const */ void QGraphicsLayoutItem::setPreferredHeight(qreal height) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rheight(); - if (height == userSizeHint) - return; - userSizeHint = height; - updateGeometry(); + d_ptr->setSizeComponent(Qt::PreferredSize, d_ptr->Height, height); } /*! @@ -500,12 +537,7 @@ void QGraphicsLayoutItem::setPreferredHeight(qreal height) */ void QGraphicsLayoutItem::setPreferredWidth(qreal width) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rwidth(); - if (width == userSizeHint) - return; - userSizeHint = width; - updateGeometry(); + d_ptr->setSizeComponent(Qt::PreferredSize, d_ptr->Width, width); } /*! @@ -519,12 +551,7 @@ void QGraphicsLayoutItem::setPreferredWidth(qreal width) */ void QGraphicsLayoutItem::setMaximumSize(const QSizeF &size) { - Q_D(QGraphicsLayoutItem); - if (size == d->userSizeHints[Qt::MaximumSize]) - return; - - d->userSizeHints[Qt::MaximumSize] = size; - updateGeometry(); + d_ptr->setSize(Qt::MaximumSize, size); } /*! @@ -554,12 +581,7 @@ QSizeF QGraphicsLayoutItem::maximumSize() const */ void QGraphicsLayoutItem::setMaximumWidth(qreal width) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rwidth(); - if (width == userSizeHint) - return; - userSizeHint = width; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MaximumSize, d_ptr->Width, width); } /*! @@ -569,12 +591,7 @@ void QGraphicsLayoutItem::setMaximumWidth(qreal width) */ void QGraphicsLayoutItem::setMaximumHeight(qreal height) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rheight(); - if (height == userSizeHint) - return; - userSizeHint = height; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MaximumSize, d_ptr->Height, height); } /*! @@ -732,6 +749,11 @@ QRectF QGraphicsLayoutItem::contentsRect() const */ QSizeF QGraphicsLayoutItem::effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint) const { + Q_D(const QGraphicsLayoutItem); + + if (!d->userSizeHints && constraint.isValid()) + return constraint; + // ### should respect size policy??? return d_ptr->effectiveSizeHints(constraint)[which]; } @@ -786,6 +808,8 @@ bool QGraphicsLayoutItem::isLayout() const } /*! + \since 4.6 + Returns whether a layout should delete this item in its destructor. If its true, then the layout will delete it. If its false, then it is assumed that another object has the ownership of it, and the layout won't @@ -812,6 +836,8 @@ bool QGraphicsLayoutItem::ownedByLayout() const return d_func()->ownedByLayout; } /*! + \since 4.6 + Sets whether a layout should delete this item in its destructor or not. \a ownership must be true to in order for the layout to delete it. \sa ownedByLayout() diff --git a/src/gui/graphicsview/qgraphicslayoutitem_p.h b/src/gui/graphicsview/qgraphicslayoutitem_p.h index 84a72d7..96f0712 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem_p.h +++ b/src/gui/graphicsview/qgraphicslayoutitem_p.h @@ -63,16 +63,20 @@ class Q_AUTOTEST_EXPORT QGraphicsLayoutItemPrivate { Q_DECLARE_PUBLIC(QGraphicsLayoutItem) public: - virtual ~QGraphicsLayoutItemPrivate() {} + virtual ~QGraphicsLayoutItemPrivate(); QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *parent, bool isLayout); void init(); QSizeF *effectiveSizeHints(const QSizeF &constraint) const; QGraphicsItem *parentItem() const; + void ensureUserSizeHints(); + void setSize(Qt::SizeHint which, const QSizeF &size); + enum SizeComponent { Width, Height }; + void setSizeComponent(Qt::SizeHint which, SizeComponent component, qreal value); QSizePolicy sizePolicy; QGraphicsLayoutItem *parent; - QSizeF userSizeHints[Qt::NSizeHints]; + QSizeF *userSizeHints; mutable QSizeF cachedSizeHints[Qt::NSizeHints]; mutable QSizeF cachedConstraint; diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp index 87802ab..3b037cf 100644 --- a/src/gui/graphicsview/qgraphicslinearlayout.cpp +++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp @@ -178,7 +178,7 @@ QLayoutStyleInfo QGraphicsLinearLayoutPrivate::styleInfo() const if (!wid) wid = new QWidget; QGraphicsItem *item = parentItem(); - QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : qApp->style(); + QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : QApplication::style(); return QLayoutStyleInfo(style, wid); } @@ -323,7 +323,11 @@ void QGraphicsLinearLayout::removeItem(QGraphicsLayoutItem *item) void QGraphicsLinearLayout::removeAt(int index) { Q_D(QGraphicsLinearLayout); - if (QGridLayoutItem *gridItem = d->engine.itemAt(d->gridRow(index), d->gridColumn(index))) { + if (index < 0 || index >= d->engine.itemCount()) { + qWarning("QGraphicsLinearLayout::removeAt: invalid index %d", index); + return; + } + if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) { if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem()) layoutItem->setParentLayoutItem(0); d->removeGridItem(gridItem); @@ -463,7 +467,7 @@ QSizePolicy::ControlTypes QGraphicsLinearLayout::controlTypes(LayoutSide side) c int QGraphicsLinearLayout::count() const { Q_D(const QGraphicsLinearLayout); - return d->engine.rowCount(d->orientation); + return d->engine.itemCount(); } /*! @@ -472,8 +476,12 @@ int QGraphicsLinearLayout::count() const QGraphicsLayoutItem *QGraphicsLinearLayout::itemAt(int index) const { Q_D(const QGraphicsLinearLayout); + if (index < 0 || index >= d->engine.itemCount()) { + qWarning("QGraphicsLinearLayout::itemAt: invalid index %d", index); + return 0; + } QGraphicsLayoutItem *item = 0; - if (QGridLayoutItem *gridItem = d->engine.itemAt(d->gridRow(index), d->gridColumn(index))) + if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) item = gridItem->layoutItem(); return item; } diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 65f315c..5fac6cf 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -275,7 +275,7 @@ void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber; QApplicationPrivate::sendMouseEvent(receiver, mouseEvent, alienWidget, widget, - &embeddedMouseGrabberPtr, lastWidgetUnderMouse); + &embeddedMouseGrabberPtr, lastWidgetUnderMouse, event->spontaneous()); embeddedMouseGrabber = embeddedMouseGrabberPtr; // Handle enter/leave events when last button is released from mouse @@ -451,6 +451,22 @@ void QGraphicsProxyWidgetPrivate::updateProxyGeometryFromWidget() /*! \internal +*/ +void QGraphicsProxyWidgetPrivate::updateProxyInputMethodAcceptanceFromWidget() +{ + Q_Q(QGraphicsProxyWidget); + if (!widget) + return; + + QWidget *focusWidget = widget->focusWidget(); + if (!focusWidget) + focusWidget = widget; + q->setFlag(QGraphicsItem::ItemAcceptsInputMethod, + focusWidget->testAttribute(Qt::WA_InputMethodEnabled)); +} + +/*! + \internal Embeds \a subWin as a subwindow of this proxy widget. \a subWin must be a top-level widget and a descendant of the widget managed by this proxy. A separate subproxy @@ -460,7 +476,7 @@ void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin) { QWExtra *extra; if (!((extra = subWin->d_func()->extra) && extra->proxyWidget)) { - QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func()); + QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func(), subWin->windowFlags()); subProxy->d_func()->setWidget_helper(subWin, false); } } @@ -544,6 +560,9 @@ QGraphicsProxyWidget::~QGraphicsProxyWidget() hidden or disabled after embedding is complete. The class documentation has a full overview over the shared state. + QGraphicsProxyWidget's window flags determine whether the widget, after + embedding, will be given window decorations or not. + After this function returns, QGraphicsProxyWidget will keep its state synchronized with that of \a widget whenever possible. @@ -661,10 +680,6 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto if (newWidget->testAttribute(Qt::WA_SetCursor)) q->setCursor(widget->cursor()); #endif - Qt::WFlags flags = newWidget->windowFlags(); - if (newWidget->windowType() == Qt::Window) - flags &= ~Qt::Window; - q->setWindowFlags(flags); q->setEnabled(newWidget->isEnabled()); q->setVisible(newWidget->isVisible()); q->setLayoutDirection(newWidget->layoutDirection()); @@ -691,6 +706,8 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto updateProxyGeometryFromWidget(); + updateProxyInputMethodAcceptanceFromWidget(); + // Hook up the event filter to keep the state up to date. newWidget->installEventFilter(q); QObject::connect(newWidget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot())); @@ -977,6 +994,7 @@ void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *even } #endif // QT_NO_CONTEXTMENU +#ifndef QT_NO_DRAGANDDROP /*! \reimp */ @@ -1097,6 +1115,7 @@ void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event) } #endif } +#endif /*! \reimp @@ -1302,8 +1321,8 @@ void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event) if (d->widget && d->widget->focusWidget()) { d->widget->focusWidget()->setFocus(event->reason()); return; - } - break; + } + break; } } diff --git a/src/gui/graphicsview/qgraphicsproxywidget.h b/src/gui/graphicsview/qgraphicsproxywidget.h index 7fcd863..01b5033 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.h +++ b/src/gui/graphicsview/qgraphicsproxywidget.h @@ -90,10 +90,12 @@ protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); #endif +#ifndef QT_NO_DRAGANDDROP void dragEnterEvent(QGraphicsSceneDragDropEvent *event); void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); void dragMoveEvent(QGraphicsSceneDragDropEvent *event); void dropEvent(QGraphicsSceneDragDropEvent *event); +#endif void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); diff --git a/src/gui/graphicsview/qgraphicsproxywidget_p.h b/src/gui/graphicsview/qgraphicsproxywidget_p.h index ec5400a..fbc9f6c 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget_p.h +++ b/src/gui/graphicsview/qgraphicsproxywidget_p.h @@ -102,6 +102,8 @@ public: void updateWidgetGeometryFromProxy(); void updateProxyGeometryFromWidget(); + void updateProxyInputMethodAcceptanceFromWidget(); + QPointF mapToReceiver(const QPointF &pos, const QWidget *receiver) const; enum ChangeMode { diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 247347a..da4a347 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -39,8 +39,6 @@ ** ****************************************************************************/ -static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; - /*! \class QGraphicsScene \brief The QGraphicsScene class provides a surface for managing a large @@ -219,6 +217,9 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; #include "qgraphicsview_p.h" #include "qgraphicswidget.h" #include "qgraphicswidget_p.h" +#include "qgraphicssceneindex_p.h" +#include "qgraphicsscenebsptreeindex_p.h" +#include "qgraphicsscenelinearindex_p.h" #include <QtCore/qdebug.h> #include <QtCore/qlist.h> @@ -242,6 +243,8 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; #include <QtGui/qstyleoption.h> #include <QtGui/qtooltip.h> #include <QtGui/qtransform.h> +#include <QtGui/qgesture.h> +#include <QtGui/qinputcontext.h> #include <private/qapplication_p.h> #include <private/qobject_p.h> #ifdef Q_WS_X11 @@ -250,65 +253,6 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; QT_BEGIN_NAMESPACE -static inline bool QRectF_intersects(const QRectF &s, const QRectF &r) -{ - qreal xp = s.left(); - qreal yp = s.top(); - qreal w = s.width(); - qreal h = s.height(); - qreal l1 = xp; - qreal r1 = xp; - if (w < 0) - l1 += w; - else - r1 += w; - - qreal l2 = r.left(); - qreal r2 = r.left(); - if (w < 0) - l2 += r.width(); - else - r2 += r.width(); - - if (l1 >= r2 || l2 >= r1) - return false; - - qreal t1 = yp; - qreal b1 = yp; - if (h < 0) - t1 += h; - else - b1 += h; - - qreal t2 = r.top(); - qreal b2 = r.top(); - if (r.height() < 0) - t2 += r.height(); - else - b2 += r.height(); - - return !(t1 >= b2 || t2 >= b1); -} - -// QRectF::intersects() returns false always if either the source or target -// rectangle's width or height are 0. This works around that problem. -static inline void _q_adjustRect(QRectF *rect) -{ - Q_ASSERT(rect); - if (!rect->width()) - rect->adjust(-0.00001, 0, 0.00001, 0); - if (!rect->height()) - rect->adjust(0, -0.00001, 0, 0.00001); -} - -static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) -{ - Q_ASSERT(item); - QRectF boundingRect(item->boundingRect()); - _q_adjustRect(&boundingRect); - return boundingRect; -} - static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent) { hover->setWidget(mouseEvent->widget()); @@ -328,17 +272,15 @@ static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraph QGraphicsScenePrivate::QGraphicsScenePrivate() : changedSignalMask(0), indexMethod(QGraphicsScene::BspTreeIndex), - bspTreeDepth(0), + index(0), lastItemCount(0), hasSceneRect(false), + dirtyGrowingItemsBoundingRect(true), updateAll(false), calledEmitUpdated(false), + processDirtyItemsEmitted(false), selectionChanging(0), - dirtyItemResetPending(false), - regenerateIndex(true), - purgePending(false), - indexTimerId(0), - restartIndexTimer(false), + needSortTopLevelItems(true), stickyFocus(false), hasFocus(false), focusItem(0), @@ -351,10 +293,12 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() dragDropItem(0), enterWidget(0), lastDropAction(Qt::IgnoreAction), + allItemsIgnoreHoverEvents(true), + allItemsUseDefaultCursor(true), painterStateProtection(true), sortCacheEnabled(false), - updatingSortCache(false), - style(0) + style(0), + allItemsIgnoreTouchEvents(true) { } @@ -365,6 +309,8 @@ void QGraphicsScenePrivate::init() { Q_Q(QGraphicsScene); + index = new QGraphicsSceneBspTreeIndex(q); + // Keep this index so we can check for connected slots later on. changedSignalMask = (1 << q->metaObject()->indexOfSignal("changed(QList<QRectF>)")); qApp->d_func()->scene_list.append(q); @@ -374,229 +320,31 @@ void QGraphicsScenePrivate::init() /*! \internal */ -QList<QGraphicsItem *> QGraphicsScenePrivate::estimateItemsInRect(const QRectF &rect) const -{ - const_cast<QGraphicsScenePrivate *>(this)->purgeRemovedItems(); - const_cast<QGraphicsScenePrivate *>(this)->_q_updateSortCache(); - - if (indexMethod == QGraphicsScene::BspTreeIndex) { - // ### Only do this once in a while. - QGraphicsScenePrivate *that = const_cast<QGraphicsScenePrivate *>(this); - - // Get items from BSP tree - QList<QGraphicsItem *> items = that->bspTree.items(rect); - - // Fill in with any unindexed items - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - if (!item->d_ptr->itemDiscovered && item->d_ptr->visible && !(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { - QRectF boundingRect = item->sceneBoundingRect(); - if (QRectF_intersects(boundingRect, rect)) { - item->d_ptr->itemDiscovered = 1; - items << item; - } - } - } - } - - // Reset the discovered state of all discovered items - for (int i = 0; i < items.size(); ++i) - items.at(i)->d_func()->itemDiscovered = 0; - return items; - } - - QList<QGraphicsItem *> itemsInRect; - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) - continue; - if (item->d_ptr->visible && item->effectiveOpacity() > qreal(0.0)) - itemsInRect << item; - } - } - for (int i = 0; i < indexedItems.size(); ++i) { - if (QGraphicsItem *item = indexedItems.at(i)) { - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) - continue; - if (item->d_ptr->visible && item->effectiveOpacity() > qreal(0.0)) - itemsInRect << item; - } - } - - return itemsInRect; -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::addToIndex(QGraphicsItem *item) -{ - if (indexMethod == QGraphicsScene::BspTreeIndex) { - if (item->d_func()->index != -1) { - bspTree.insertItem(item, item->sceneBoundingRect()); - foreach (QGraphicsItem *child, item->children()) - child->addToIndex(); - } else { - // The BSP tree is regenerated if the number of items grows to a - // certain threshold, or if the bounding rect of the graph doubles in - // size. - startIndexTimer(); - } - } -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::removeFromIndex(QGraphicsItem *item) -{ - if (indexMethod == QGraphicsScene::BspTreeIndex) { - int index = item->d_func()->index; - if (index != -1) { - bspTree.removeItem(item, item->sceneBoundingRect()); - freeItemIndexes << index; - indexedItems[index] = 0; - item->d_func()->index = -1; - unindexedItems << item; - - foreach (QGraphicsItem *child, item->children()) - child->removeFromIndex(); - } - - startIndexTimer(); - } -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::resetIndex() +QGraphicsScenePrivate *QGraphicsScenePrivate::get(QGraphicsScene *q) { - purgeRemovedItems(); - if (indexMethod == QGraphicsScene::BspTreeIndex) { - for (int i = 0; i < indexedItems.size(); ++i) { - if (QGraphicsItem *item = indexedItems.at(i)) { - item->d_ptr->index = -1; - unindexedItems << item; - } - } - indexedItems.clear(); - freeItemIndexes.clear(); - regenerateIndex = true; - startIndexTimer(); - } + return q->d_func(); } -static inline int intmaxlog(int n) -{ - return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0); -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::_q_updateIndex() +void QGraphicsScenePrivate::_q_emitUpdated() { - if (!indexTimerId) - return; - Q_Q(QGraphicsScene); - q->killTimer(indexTimerId); - indexTimerId = 0; - - purgeRemovedItems(); - - // Add unindexedItems to indexedItems - QRectF unindexedItemsBoundingRect; - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - unindexedItemsBoundingRect |= item->sceneBoundingRect(); - if (!freeItemIndexes.isEmpty()) { - int freeIndex = freeItemIndexes.takeFirst(); - item->d_func()->index = freeIndex; - indexedItems[freeIndex] = item; - } else { - item->d_func()->index = indexedItems.size(); - indexedItems << item; - } - } - } - - // Update growing scene rect. - QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; - growingItemsBoundingRect |= unindexedItemsBoundingRect; - - // Determine whether we should regenerate the BSP tree. - if (indexMethod == QGraphicsScene::BspTreeIndex) { - int depth = bspTreeDepth; - if (depth == 0) { - int oldDepth = intmaxlog(lastItemCount); - depth = intmaxlog(indexedItems.size()); - static const int slack = 100; - if (bspTree.leafCount() == 0 || (oldDepth != depth && qAbs(lastItemCount - indexedItems.size()) > slack)) { - // ### Crude algorithm. - regenerateIndex = true; - } - } - - // Regenerate the tree. - if (regenerateIndex) { - regenerateIndex = false; - bspTree.initialize(sceneRect, depth); - unindexedItems = indexedItems; - lastItemCount = indexedItems.size(); - q->update(); - - // Take this opportunity to reset our largest-item counter for - // untransformable items. When the items are inserted into the BSP - // tree, we'll get an accurate calculation. - largestUntransformableItem = QRectF(); - } - } + calledEmitUpdated = false; - // Insert all unindexed items into the tree. - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - QRectF rect = item->sceneBoundingRect(); - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) - continue; - if (indexMethod == QGraphicsScene::BspTreeIndex) - bspTree.insertItem(item, rect); - - // If the item ignores view transformations, update our - // largest-item-counter to ensure that the view can accurately - // discover untransformable items when drawing. - if (item->d_ptr->itemIsUntransformable()) { - QGraphicsItem *topmostUntransformable = item; - while (topmostUntransformable && (topmostUntransformable->d_ptr->ancestorFlags - & QGraphicsItemPrivate::AncestorIgnoresTransformations)) { - topmostUntransformable = topmostUntransformable->parentItem(); - } - // ### Verify that this is the correct largest untransformable rectangle. - largestUntransformableItem |= item->mapToItem(topmostUntransformable, item->boundingRect()).boundingRect(); - } + if (dirtyGrowingItemsBoundingRect) { + if (!hasSceneRect) { + const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; + growingItemsBoundingRect |= q->itemsBoundingRect(); + if (oldGrowingItemsBoundingRect != growingItemsBoundingRect) + emit q->sceneRectChanged(growingItemsBoundingRect); } + dirtyGrowingItemsBoundingRect = false; } - unindexedItems.clear(); - - // Notify scene rect changes. - if (!hasSceneRect && growingItemsBoundingRect != oldGrowingItemsBoundingRect) - emit q->sceneRectChanged(growingItemsBoundingRect); -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::_q_emitUpdated() -{ - Q_Q(QGraphicsScene); - calledEmitUpdated = false; // Ensure all views are connected if anything is connected. This disables // the optimization that items send updates directly to the views, but it // needs to happen in order to keep compatibility with the behavior from // Qt 4.4 and backward. - if (!views.isEmpty() && (connectedSignals & changedSignalMask)) { + if (connectedSignals[0] & changedSignalMask) { for (int i = 0; i < views.size(); ++i) { QGraphicsView *view = views.at(i); if (!view->d_func()->connectedToScene) { @@ -605,13 +353,16 @@ void QGraphicsScenePrivate::_q_emitUpdated() views.at(i), SLOT(updateScene(QList<QRectF>))); } } + } else { + updateAll = false; + for (int i = 0; i < views.size(); ++i) + views.at(i)->d_func()->processPendingUpdates(); + // It's important that we update all views before we dispatch, hence two for-loops. + for (int i = 0; i < views.size(); ++i) + views.at(i)->d_func()->dispatchPendingUpdateRequests(); + return; } - // Ensure all dirty items's current positions are recorded in the list of - // updated rects. - for (int i = 0; i < dirtyItems.size(); ++i) - updatedRects += dirtyItems.at(i)->sceneBoundingRect(); - // Notify the changes to anybody interested. QList<QRectF> oldUpdatedRects; oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects; @@ -622,15 +373,24 @@ void QGraphicsScenePrivate::_q_emitUpdated() /*! \internal +*/ +void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item) +{ + needSortTopLevelItems = true; + item->d_ptr->siblingIndex = topLevelItems.size(); + topLevelItems.append(item); +} - Updates all items in the pending update list. At this point, the list is - unlikely to contain partially constructed items. +/*! + \internal */ -void QGraphicsScenePrivate::_q_updateLater() +void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item) { - foreach (QGraphicsItem *item, pendingUpdateItems) - item->update(); - pendingUpdateItems.clear(); + topLevelItems.removeOne(item); + // NB! Do not use topLevelItems.removeAt(item->d_ptr->siblingIndex) because + // the item is not guaranteed to be at the index after the list is sorted + // (see ensureSortedTopLevelItems()). + item->d_ptr->siblingIndex = -1; } /*! @@ -652,70 +412,103 @@ void QGraphicsScenePrivate::_q_polishItems() unpolishedItems.clear(); } -/*! - \internal -*/ -void QGraphicsScenePrivate::_q_resetDirtyItems() +void QGraphicsScenePrivate::_q_processDirtyItems() { - for (int i = 0; i < dirtyItems.size(); ++i) { - QGraphicsItem *item = dirtyItems.at(i); - item->d_ptr->dirty = 0; - item->d_ptr->dirtyChildren = 0; + processDirtyItemsEmitted = false; + + if (updateAll) { + Q_ASSERT(calledEmitUpdated); + // No need for further processing (except resetting the dirty states). + // The growingItemsBoundingRect is updated in _q_emitUpdated. + for (int i = 0; i < topLevelItems.size(); ++i) + resetDirtyItem(topLevelItems.at(i), /*recursive=*/true); + return; } - dirtyItems.clear(); - dirtyItemResetPending = false; -} -/*! - \internal -*/ -void QGraphicsScenePrivate::resetDirtyItemsLater() -{ - Q_Q(QGraphicsScene); - if (dirtyItemResetPending) + const bool wasPendingSceneUpdate = calledEmitUpdated; + const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; + + // Process items recursively. + for (int i = 0; i < topLevelItems.size(); ++i) + processDirtyItemsRecursive(topLevelItems.at(i)); + + dirtyGrowingItemsBoundingRect = false; + if (!hasSceneRect && oldGrowingItemsBoundingRect != growingItemsBoundingRect) + emit q_func()->sceneRectChanged(growingItemsBoundingRect); + + if (wasPendingSceneUpdate) return; - // dirtyItems.reserve(indexedItems.size() + unindexedItems.size()); - dirtyItemResetPending = true; - QMetaObject::invokeMethod(q, "_q_resetDirtyItems", Qt::QueuedConnection); + + for (int i = 0; i < views.size(); ++i) + views.at(i)->d_func()->processPendingUpdates(); + + if (calledEmitUpdated) { + // We did a compatibility QGraphicsScene::update in processDirtyItemsRecursive + // and we cannot wait for the control to reach the eventloop before the + // changed signal is emitted, so we emit it now. + _q_emitUpdated(); + } + + // Immediately dispatch all pending update requests on the views. + for (int i = 0; i < views.size(); ++i) + views.at(i)->d_func()->dispatchPendingUpdateRequests(); } /*! \internal Schedules an item for removal. This function leaves some stale indexes - around in the BSP tree; these will be cleaned up the next time someone - triggers purgeRemovedItems(). + around in the BSP tree if called from the item's destructor; these will + be cleaned up the next time someone triggers purgeRemovedItems(). - Note: This function is called from QGraphicsItem's destructor. \a item is + Note: This function might get called from QGraphicsItem's destructor. \a item is being destroyed, so we cannot call any pure virtual functions on it (such as boundingRect()). Also, it is unnecessary to update the item's own state in any way. - - ### Refactoring: This function shares much functionality with removeItem() */ -void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) +void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) { Q_Q(QGraphicsScene); - // Clear focus on the item to remove any reference in the focusWidget - // chain. + // Clear focus on the item to remove any reference in the focusWidget chain. item->clearFocus(); - int index = item->d_func()->index; - if (index != -1) { - // Important: The index is useless until purgeRemovedItems() is - // called. - indexedItems[index] = (QGraphicsItem *)0; - if (!purgePending) { - purgePending = true; - q->update(); + markDirty(item, QRectF(), false, false, false, false, /*removingItemFromScene=*/true); + + if (item->d_ptr->inDestructor) { + // The item is actually in its destructor, we call the special method in the index. + index->deleteItem(item); + } else { + // Can potentially call item->boundingRect() (virtual function), that's why + // we only can call this function if the item is not in its destructor. + index->removeItem(item); + } + + item->d_ptr->clearSubFocus(); + + if (!item->d_ptr->inDestructor && item == tabFocusFirst) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + widget->d_func()->fixFocusChainBeforeReparenting(0, 0); + } + + item->d_func()->scene = 0; + + // Unregister focus proxy. + QMultiHash<QGraphicsItem *, QGraphicsItem *>::iterator it = focusProxyReverseMap.find(item); + while (it != focusProxyReverseMap.end() && it.key() == item) { + it.value()->d_ptr->focusProxy = 0; + it = focusProxyReverseMap.erase(it); + } + + // Remove from parent, or unregister from toplevels. + if (QGraphicsItem *parentItem = item->parentItem()) { + if (parentItem->scene()) { + Q_ASSERT_X(parentItem->scene() == q, "QGraphicsScene::removeItem", + "Parent item's scene is different from this item's scene"); + item->d_ptr->setParentItemHelper(0); } - removedItems << item; } else { - // Recently added items are purged immediately. unindexedItems() never - // contains stale items. - unindexedItems.removeAll(item); - q->update(); + unregisterTopLevelItem(item); } // Reset the mouse grabber and focus item data. @@ -735,10 +528,9 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) // Update selected & hovered item bookkeeping selectedItems.remove(item); hoverItems.removeAll(item); - pendingUpdateItems.removeAll(item); cachedItemsUnderMouse.removeAll(item); unpolishedItems.removeAll(item); - dirtyItems.removeAll(item); + resetDirtyItem(item); //We remove all references of item from the sceneEventFilter arrays QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin(); @@ -749,24 +541,19 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) ++iterator; } - // Remove from scene transform cache - int transformIndex = item->d_func()->sceneTransformIndex; - if (transformIndex != -1) { - validTransforms.setBit(transformIndex, 0); - freeSceneTransformSlots.append(transformIndex); + if (!item->d_ptr->inDestructor) { + // Remove all children recursively + for (int i = 0; i < item->d_ptr->children.size(); ++i) + q->removeItem(item->d_ptr->children.at(i)); } - // Remove all children recursively. - foreach (QGraphicsItem *child, item->children()) - _q_removeItemLater(child); - - // Reset the mouse grabber + // Reset the mouse grabber and focus item data. if (mouseGrabberItems.contains(item)) - ungrabMouse(item, /* item is dying */ true); + ungrabMouse(item, /* item is dying */ item->d_ptr->inDestructor); // Reset the keyboard grabber if (keyboardGrabberItems.contains(item)) - ungrabKeyboard(item, /* item is dying */ true); + ungrabKeyboard(item, /* item is dying */ item->d_ptr->inDestructor); // Reset the last mouse grabber item if (item == lastMouseGrabberItem) @@ -780,46 +567,66 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) /*! \internal - - Removes stale pointers from all data structures. */ -void QGraphicsScenePrivate::purgeRemovedItems() +void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, + Qt::FocusReason focusReason) { Q_Q(QGraphicsScene); - - if (!purgePending && removedItems.isEmpty()) + if (item == focusItem) return; - // Remove stale items from the BSP tree. - if (indexMethod != QGraphicsScene::NoIndex) - bspTree.removeItems(removedItems); + // Clear focus if asked to set focus on something that can't + // accept input focus. + if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable) + || !item->isVisible() || !item->isEnabled())) { + item = 0; + } - // Purge this list. - removedItems.clear(); - freeItemIndexes.clear(); - for (int i = 0; i < indexedItems.size(); ++i) { - if (!indexedItems.at(i)) - freeItemIndexes << i; + // Set focus on the scene if an item requests focus. + if (item) { + q->setFocus(focusReason); + if (item == focusItem) + return; } - purgePending = false; - // No locality info for the items; update the whole scene. - q->update(); -} + // Auto-update focus proxy. The closest parent that detects + // focus proxies is updated as the proxy gains or loses focus. + if (item) { + QGraphicsItem *p = item->d_ptr->parent; + while (p) { + if (p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) { + p->setFocusProxy(item); + break; + } + p = p->d_ptr->parent; + } + } -/*! - \internal + if (focusItem) { + QFocusEvent event(QEvent::FocusOut, focusReason); + lastFocusItem = focusItem; + focusItem = 0; + sendEvent(lastFocusItem, &event); - Starts or restarts the timer used for reindexing unindexed items. -*/ -void QGraphicsScenePrivate::startIndexTimer() -{ - Q_Q(QGraphicsScene); - if (indexTimerId) { - restartIndexTimer = true; - } else { - indexTimerId = q->startTimer(QGRAPHICSSCENE_INDEXTIMER_TIMEOUT); + if (lastFocusItem + && (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { + // Reset any visible preedit text + QInputMethodEvent imEvent; + sendEvent(lastFocusItem, &imEvent); + + // Close any external input method panel + for (int i = 0; i < views.size(); ++i) + views.at(i)->inputContext()->reset(); + } } + + if (item) { + focusItem = item; + QFocusEvent event(QEvent::FocusIn, focusReason); + sendEvent(item, &event); + } + + updateInputMethodSensitivityInViews(); } /*! @@ -880,11 +687,18 @@ void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit) { // Append to list of mouse grabber items, and send a mouse grab event. if (mouseGrabberItems.contains(item)) { - if (mouseGrabberItems.last() == item) - qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); - else + if (mouseGrabberItems.last() == item) { + Q_ASSERT(!implicit); + if (!lastMouseGrabberItemHasImplicitMouseGrab) { + qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); + } else { + // Upgrade to an explicit mouse grab + lastMouseGrabberItemHasImplicitMouseGrab = false; + } + } else { qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p", mouseGrabberItems.last()); + } return; } @@ -1036,6 +850,12 @@ void QGraphicsScenePrivate::clearKeyboardGrabber() ungrabKeyboard(keyboardGrabberItems.first()); } +void QGraphicsScenePrivate::enableMouseTrackingOnViews() +{ + foreach (QGraphicsView *view, views) + view->viewport()->setMouseTracking(true); +} + /*! Returns all items for the screen position in \a event. */ @@ -1045,41 +865,20 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &scre { Q_Q(const QGraphicsScene); QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0; - QList<QGraphicsItem *> items; - if (view) - items = view->items(view->viewport()->mapFromGlobal(screenPos)); - else - items = q->items(scenePos); - return items; -} + if (!view) + return q->items(scenePos, Qt::IntersectsItemShape, Qt::AscendingOrder, QTransform()); -/*! - \internal + const QRectF pointRect(QPointF(widget->mapFromGlobal(screenPos)), QSizeF(1, 1)); + if (!view->isTransformed()) + return q->items(pointRect, Qt::IntersectsItemShape, Qt::AscendingOrder); - Checks if item collides with the path and mode, but also checks that if it - doesn't collide, maybe its frame rect will. -*/ -bool QGraphicsScenePrivate::itemCollidesWithPath(QGraphicsItem *item, - const QPainterPath &path, - Qt::ItemSelectionMode mode) -{ - if (item->collidesWithPath(path, mode)) - return true; - if (item->isWidget()) { - // Check if this is a window, and if its frame rect collides. - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); - if (widget->isWindow()) { - QRectF frameRect = widget->windowFrameRect(); - QPainterPath framePath; - framePath.addRect(frameRect); - bool intersects = path.intersects(frameRect); - if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect) - return intersects || path.contains(frameRect.topLeft()) - || framePath.contains(path.elementAt(0)); - return !intersects && path.contains(frameRect.topLeft()); - } + const QTransform viewTransform = view->viewportTransform(); + if (viewTransform.type() <= QTransform::TxScale) { + return q->items(viewTransform.inverted().mapRect(pointRect), Qt::IntersectsItemShape, + Qt::AscendingOrder, viewTransform); } - return false; + return q->items(viewTransform.inverted().map(pointRect), Qt::IntersectsItemShape, + Qt::AscendingOrder, viewTransform); } /*! @@ -1091,7 +890,7 @@ void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouse if (event->buttons() & i) { mouseGrabberButtonDownPos.insert(Qt::MouseButton(i), mouseGrabberItems.last()->d_ptr->genericMapFromScene(event->scenePos(), - event->widget())); + event->widget())); mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos()); mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos()); } @@ -1125,6 +924,24 @@ void QGraphicsScenePrivate::removeSceneEventFilter(QGraphicsItem *watched, QGrap } /*! + \internal +*/ +bool QGraphicsScenePrivate::filterDescendantEvent(QGraphicsItem *item, QEvent *event) +{ + if (item && (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents)) { + QGraphicsItem *parent = item->parentItem(); + while (parent) { + if (parent->d_ptr->filtersDescendantEvents && parent->sceneEventFilter(item, event)) + return true; + if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents)) + return false; + parent = parent->parentItem(); + } + } + return false; +} + +/*! \internal */ bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event) @@ -1156,7 +973,9 @@ bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event) { if (filterEvent(item, event)) return false; - return (item && item->isEnabled()) ? item->sceneEvent(event) : false; + if (filterDescendantEvent(item, event)) + return false; + return (item && item->isEnabled() ? item->sceneEvent(event) : false); } /*! @@ -1294,12 +1113,14 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // check if the item we are sending to are disabled (before we send the event) bool disabled = !item->isEnabled(); bool isWindow = item->isWindow(); - if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick && item != lastMouseGrabberItem) { + if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick + && item != lastMouseGrabberItem && lastMouseGrabberItem) { // If this item is different from the item that received the last // mouse event, and mouseEvent is a doubleclick event, then the // event is converted to a press. Known limitation: // Triple-clicking will not generate a doubleclick, though. QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress); + mousePress.spont = mouseEvent->spont; mousePress.accept(); mousePress.setButton(mouseEvent->button()); mousePress.setButtons(mouseEvent->buttons()); @@ -1367,614 +1188,6 @@ QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item) return 0; } - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPointF &pos) const -{ - QList<QGraphicsItem *> items; - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - QRectF adjustedRect = QRectF(pos, QSize(1,1)); - foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - // Rect intersects/contains item's shape - if (QRectF_intersects(adjustedRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (item->contains(xinv.map(pos))) { - items << item; - keep = true; - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - // Recurse into children that clip children. - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) - childItems_helper(&items, item, xinv.map(pos)); - } - } - - sortItems(&items, Qt::AscendingOrder, sortCacheEnabled); - return items; -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - QList<QGraphicsItem *> items; - - QPainterPath path; - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - QRectF adjustedRect(rect); - _q_adjustRect(&adjustedRect); - foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Rect intersects/contains item's bounding rect - QRectF mbr = x.mapRect(br); - if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr)) - || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(mbr))) { - items << item; - keep = true; - } - } else { - // Rect intersects/contains item's shape - if (QRectF_intersects(adjustedRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (path.isEmpty()) - path.addRect(rect); - if (itemCollidesWithPath(item, xinv.map(path), mode)) { - items << item; - keep = true; - } - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - // Recurse into children that clip children. - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (x.type() <= QTransform::TxScale) { - // Rect - childItems_helper(&items, item, xinv.mapRect(rect), mode); - } else { - // Polygon - childItems_helper(&items, item, xinv.map(rect), mode); - } - } - } - } - - if (order != Qt::SortOrder(-1)) - sortItems(&items, order, sortCacheEnabled); - return items; -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPolygonF &polygon, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - QList<QGraphicsItem *> items; - - QRectF polyRect(polygon.boundingRect()); - _q_adjustRect(&polyRect); - QPainterPath path; - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - foreach (QGraphicsItem *item, estimateItemsInRect(polyRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Polygon contains/intersects item's bounding rect - if (path == QPainterPath()) - path.addPolygon(polygon); - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) { - items << item; - keep = true; - } - } else { - // Polygon contains/intersects item's shape - if (QRectF_intersects(polyRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (path == QPainterPath()) - path.addPolygon(polygon); - if (itemCollidesWithPath(item, xinv.map(path), mode)) { - items << item; - keep = true; - } - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - // Recurse into children that clip children. - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) - childItems_helper(&items, item, xinv.map(polygon), mode); - } - } - - if (order != Qt::SortOrder(-1)) - sortItems(&items, order, sortCacheEnabled); - return items; -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPainterPath &path, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - QList<QGraphicsItem *> items; - QRectF pathRect(path.controlPointRect()); - _q_adjustRect(&pathRect); - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - foreach (QGraphicsItem *item, estimateItemsInRect(pathRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Path contains/intersects item's bounding rect - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) { - items << item; - keep = true; - } - } else { - // Path contains/intersects item's shape - if (QRectF_intersects(pathRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (itemCollidesWithPath(item, xinv.map(path), mode)) { - items << item; - keep = true; - } - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) - childItems_helper(&items, item, xinv.map(path), mode); - } - } - - if (order != Qt::SortOrder(-1)) - sortItems(&items, order, sortCacheEnabled); - return items; -} - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPointF &pos) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - // ### is this needed? - if (parentClip && !parent->boundingRect().contains(pos)) - return; - - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->hasTransform && !item->transform().isInvertible()) - continue; - - // Skip invisible items and all their children. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - if (item->contains(item->mapFromParent(pos))) { - items->append(item); - keep = true; - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) - // Recurse into children. - childItems_helper(items, item, item->mapFromParent(pos)); - } -} - - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QRectF &rect, - Qt::ItemSelectionMode mode) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - QRectF adjustedRect(rect); - _q_adjustRect(&adjustedRect); - QRectF r = !parentClip ? adjustedRect : adjustedRect.intersected(adjustedItemBoundingRect(parent)); - if (r.isEmpty()) - return; - - QPainterPath path; - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->hasTransform && !item->transform().isInvertible()) - continue; - - // Skip invisible items and all their children. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - QRectF mbr = item->mapRectToParent(br); - if (mode >= Qt::ContainsItemBoundingRect) { - // Rect intersects/contains item's bounding rect - if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr)) - || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(br))) { - items->append(item); - keep = true; - } - } else { - // Rect intersects/contains item's shape - if (QRectF_intersects(rect, mbr)) { - if (path == QPainterPath()) - path.addRect(rect); - if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { - items->append(item); - keep = true; - } - } - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { - // Recurse into children. - if (!item->d_ptr->hasTransform || item->transform().type() <= QTransform::TxScale) { - // Rect - childItems_helper(items, item, item->mapRectFromParent(rect), mode); - } else { - // Polygon - childItems_helper(items, item, item->mapFromParent(rect), mode); - } - } - } -} - - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPolygonF &polygon, - Qt::ItemSelectionMode mode) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - QRectF polyRect(polygon.boundingRect()); - _q_adjustRect(&polyRect); - QRectF r = !parentClip ? polyRect : polyRect.intersected(adjustedItemBoundingRect(parent)); - if (r.isEmpty()) - return; - - QPainterPath path; - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->hasTransform && !item->transform().isInvertible()) - continue; - - // Skip invisible items. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Polygon contains/intersects item's bounding rect - if (path == QPainterPath()) - path.addPolygon(polygon); - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) { - items->append(item); - keep = true; - } - } else { - // Polygon contains/intersects item's shape - if (QRectF_intersects(polyRect, item->mapRectToParent(br))) { - if (path == QPainterPath()) - path.addPolygon(polygon); - if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { - items->append(item); - keep = true; - } - } - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { - // Recurse into children that clip children. - childItems_helper(items, item, item->mapFromParent(polygon), mode); - } - } -} - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPainterPath &path, - Qt::ItemSelectionMode mode) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - QRectF pathRect(path.boundingRect()); - _q_adjustRect(&pathRect); - QRectF r = !parentClip ? pathRect : pathRect.intersected(adjustedItemBoundingRect(parent)); - if (r.isEmpty()) - return; - - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->hasTransform && !item->transform().isInvertible()) - continue; - - // Skip invisible items. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Polygon contains/intersects item's bounding rect - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) { - items->append(item); - keep = true; - } - } else { - // Path contains/intersects item's shape - if (QRectF_intersects(pathRect, item->mapRectToParent(br))) { - if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { - items->append(item); - keep = true; - } - } - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { - // Recurse into children that clip children. - childItems_helper(items, item, item->mapFromParent(path), mode); - } - } -} - -void QGraphicsScenePrivate::invalidateSortCache() -{ - Q_Q(QGraphicsScene); - if (!sortCacheEnabled || updatingSortCache) - return; - - updatingSortCache = true; - QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection); -} - -/*! - \internal - - Should not be exported, but we can't change that now. - ### Qt 5: Remove symbol / make static -*/ -inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - // Return true if sibling item1 is on top of item2. - const QGraphicsItemPrivate *d1 = item1->d_ptr; - const QGraphicsItemPrivate *d2 = item2->d_ptr; - bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent; - bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent; - if (f1 != f2) return f2; - qreal z1 = d1->z; - qreal z2 = d2->z; - return z1 != z2 ? z1 > z2 : item1 > item2; -} - -/*! - \internal - - Should not be exported, but we can't change that now. -*/ -inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - return QGraphicsScenePrivate::closestItemFirst_withoutCache(item1, item2); -} - -/*! - Returns true if \a item1 is on top of \a item2. - - \internal -*/ -bool QGraphicsScenePrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - // Siblings? Just check their z-values. - const QGraphicsItemPrivate *d1 = item1->d_ptr; - const QGraphicsItemPrivate *d2 = item2->d_ptr; - if (d1->parent == d2->parent) - return qt_closestLeaf(item1, item2); - - // Find common ancestor, and each item's ancestor closest to the common - // ancestor. - int item1Depth = d1->depth; - int item2Depth = d2->depth; - const QGraphicsItem *p = item1; - const QGraphicsItem *t1 = item1; - while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { - if (p == item2) { - // item2 is one of item1's ancestors; item1 is on top - return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); - } - t1 = p; - --item1Depth; - } - p = item2; - const QGraphicsItem *t2 = item2; - while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { - if (p == item1) { - // item1 is one of item2's ancestors; item1 is not on top - return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); - } - t2 = p; - --item2Depth; - } - - // item1Ancestor is now at the same level as item2Ancestor, but not the same. - const QGraphicsItem *a1 = t1; - const QGraphicsItem *a2 = t2; - while (a1) { - const QGraphicsItem *p1 = a1; - const QGraphicsItem *p2 = a2; - a1 = a1->parentItem(); - a2 = a2->parentItem(); - if (a1 && a1 == a2) - return qt_closestLeaf(p1, p2); - } - - // No common ancestor? Then just compare the items' toplevels directly. - return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem()); -} - -/*! - Returns true if \a item2 is on top of \a item1. - - \internal -*/ -bool QGraphicsScenePrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - return closestItemFirst_withoutCache(item2, item1); -} - -void QGraphicsScenePrivate::climbTree(QGraphicsItem *item, int *stackingOrder) -{ - if (!item->d_ptr->children.isEmpty()) { - QList<QGraphicsItem *> childList = item->d_ptr->children; - qSort(childList.begin(), childList.end(), qt_closestLeaf); - for (int i = 0; i < childList.size(); ++i) { - QGraphicsItem *item = childList.at(i); - if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent)) - climbTree(childList.at(i), stackingOrder); - } - item->d_ptr->globalStackingOrder = (*stackingOrder)++; - for (int i = 0; i < childList.size(); ++i) { - QGraphicsItem *item = childList.at(i); - if (item->flags() & QGraphicsItem::ItemStacksBehindParent) - climbTree(childList.at(i), stackingOrder); - } - } else { - item->d_ptr->globalStackingOrder = (*stackingOrder)++; - } -} - -void QGraphicsScenePrivate::_q_updateSortCache() -{ - _q_updateIndex(); - - if (!sortCacheEnabled || !updatingSortCache) - return; - - updatingSortCache = false; - int stackingOrder = 0; - - QList<QGraphicsItem *> topLevels; - - for (int i = 0; i < indexedItems.size(); ++i) { - QGraphicsItem *item = indexedItems.at(i); - if (item && item->parentItem() == 0) - topLevels << item; - } - for (int i = 0; i < unindexedItems.size(); ++i) { - QGraphicsItem *item = unindexedItems.at(i); - if (item->parentItem() == 0) - topLevels << item; - } - - qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf); - for (int i = 0; i < topLevels.size(); ++i) - climbTree(topLevels.at(i), &stackingOrder); -} - -void QGraphicsScenePrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, - bool sortCacheEnabled) -{ - if (sortCacheEnabled) { - if (order == Qt::AscendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache); - } else if (order == Qt::DescendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemLast_withCache); - } - } else { - if (order == Qt::AscendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); - } else if (order == Qt::DescendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); - } - } -} - /*! \internal @@ -1996,7 +1209,7 @@ void QGraphicsScenePrivate::setFont_helper(const QFont &font) */ void QGraphicsScenePrivate::resolveFont() { - QFont naturalFont = qApp->font(); + QFont naturalFont = QApplication::font(); naturalFont.resolve(0); QFont resolvedFont = font.resolve(naturalFont); updateFont(resolvedFont); @@ -2052,7 +1265,7 @@ void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette) */ void QGraphicsScenePrivate::resolvePalette() { - QPalette naturalPalette = qApp->palette(); + QPalette naturalPalette = QApplication::palette(); naturalPalette.resolve(0); QPalette resolvedPalette = palette.resolve(naturalPalette); updatePalette(resolvedPalette); @@ -2107,8 +1320,8 @@ QGraphicsScene::QGraphicsScene(QObject *parent) QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent) : QObject(*new QGraphicsScenePrivate, parent) { - setSceneRect(sceneRect); d_func()->init(); + setSceneRect(sceneRect); } /*! @@ -2122,8 +1335,8 @@ QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent) QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent) : QObject(*new QGraphicsScenePrivate, parent) { - setSceneRect(x, y, width, height); d_func()->init(); + setSceneRect(x, y, width, height); } /*! @@ -2160,8 +1373,19 @@ QGraphicsScene::~QGraphicsScene() QRectF QGraphicsScene::sceneRect() const { Q_D(const QGraphicsScene); - const_cast<QGraphicsScenePrivate *>(d)->_q_updateIndex(); - return d->hasSceneRect ? d->sceneRect : d->growingItemsBoundingRect; + if (d->hasSceneRect) + return d->sceneRect; + + if (d->dirtyGrowingItemsBoundingRect) { + // Lazily update the growing items bounding rect + QGraphicsScenePrivate *thatd = const_cast<QGraphicsScenePrivate *>(d); + QRectF oldGrowingBoundingRect = thatd->growingItemsBoundingRect; + thatd->growingItemsBoundingRect |= itemsBoundingRect(); + thatd->dirtyGrowingItemsBoundingRect = false; + if (oldGrowingBoundingRect != thatd->growingItemsBoundingRect) + emit const_cast<QGraphicsScene *>(this)->sceneRectChanged(thatd->growingItemsBoundingRect); + } + return d->growingItemsBoundingRect; } void QGraphicsScene::setSceneRect(const QRectF &rect) { @@ -2169,8 +1393,7 @@ void QGraphicsScene::setSceneRect(const QRectF &rect) if (rect != d->sceneRect) { d->hasSceneRect = !rect.isNull(); d->sceneRect = rect; - d->resetIndex(); - emit sceneRectChanged(rect); + emit sceneRectChanged(d->hasSceneRect ? rect : d->growingItemsBoundingRect); } } @@ -2211,7 +1434,7 @@ void QGraphicsScene::setSceneRect(const QRectF &rect) void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode) { - Q_D(QGraphicsScene); + // ### Switch to using the recursive rendering algorithm instead. // Default source rect = scene rect QRectF sourceRect = source; @@ -2269,36 +1492,8 @@ void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRect // Generate the style options QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems]; - for (int i = 0; i < numItems; ++i) { - QGraphicsItem *item = itemArray[i]; - - QStyleOptionGraphicsItem option; - option.state = QStyle::State_None; - option.rect = item->boundingRect().toRect(); - if (item->isSelected()) - option.state |= QStyle::State_Selected; - if (item->isEnabled()) - option.state |= QStyle::State_Enabled; - if (item->hasFocus()) - option.state |= QStyle::State_HasFocus; - if (d->hoverItems.contains(item)) - option.state |= QStyle::State_MouseOver; - if (item == mouseGrabberItem()) - option.state |= QStyle::State_Sunken; - - // Calculate a simple level-of-detail metric. - // ### almost identical code in QGraphicsView::paintEvent() - // and QGraphicsView::render() - consider refactoring - QTransform itemToDeviceTransform = item->deviceTransform(painterTransform); - - option.levelOfDetail = qSqrt(itemToDeviceTransform.map(v1).length() * itemToDeviceTransform.map(v2).length()); - option.matrix = itemToDeviceTransform.toAffine(); //### discards perspective - - option.exposedRect = item->boundingRect(); - option.exposedRect &= itemToDeviceTransform.inverted().mapRect(targetRect); - - styleOptionArray[i] = option; - } + for (int i = 0; i < numItems; ++i) + itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterTransform, targetRect.toRect()); // Render the scene. drawBackground(painter, sourceRect); @@ -2335,8 +1530,19 @@ QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method) { Q_D(QGraphicsScene); - d->resetIndex(); + if (d->indexMethod == method) + return; + d->indexMethod = method; + + QList<QGraphicsItem *> oldItems = d->index->items(Qt::AscendingOrder); + delete d->index; + if (method == BspTreeIndex) + d->index = new QGraphicsSceneBspTreeIndex(this); + else + d->index = new QGraphicsSceneLinearIndex(this); + for (int i = oldItems.size() - 1; i >= 0; --i) + d->index->addItem(oldItems.at(i)); } /*! @@ -2374,35 +1580,32 @@ void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method) int QGraphicsScene::bspTreeDepth() const { Q_D(const QGraphicsScene); - return d->bspTreeDepth; + QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index); + return bspTree ? bspTree->bspTreeDepth() : 0; } void QGraphicsScene::setBspTreeDepth(int depth) { Q_D(QGraphicsScene); - if (d->bspTreeDepth == depth) - return; - if (depth < 0) { qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth); return; } - d->bspTreeDepth = depth; - d->resetIndex(); + QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index); + if (!bspTree) { + qWarning("QGraphicsScene::setBspTreeDepth: can not apply if indexing method is not BSP"); + return; + } + bspTree->setBspTreeDepth(depth); } /*! \property QGraphicsScene::sortCacheEnabled \brief whether sort caching is enabled \since 4.5 + \obsolete - When enabled, this property adds a cache that speeds up sorting and - transformations for scenes with deep hierarchies (i.e., items with many - levels of descendents), at the cost of using more memory (approx. 100 more - bytes of memory per item). - - Items that are not part of a deep hierarchy suffer no penalty from this - cache. + Since Qt 4.6, this property has no effect. */ bool QGraphicsScene::isSortCacheEnabled() const { @@ -2412,10 +1615,9 @@ bool QGraphicsScene::isSortCacheEnabled() const void QGraphicsScene::setSortCacheEnabled(bool enabled) { Q_D(QGraphicsScene); - if (enabled == d->sortCacheEnabled) + if (d->sortCacheEnabled == enabled) return; - if ((d->sortCacheEnabled = enabled)) - d->invalidateSortCache(); + d->sortCacheEnabled = enabled; } /*! @@ -2427,6 +1629,7 @@ void QGraphicsScene::setSortCacheEnabled(bool enabled) */ QRectF QGraphicsScene::itemsBoundingRect() const { + // Does not take untransformable items into account. QRectF boundingRect; foreach (QGraphicsItem *item, items()) boundingRect |= item->sceneBoundingRect(); @@ -2441,44 +1644,43 @@ QRectF QGraphicsScene::itemsBoundingRect() const QList<QGraphicsItem *> QGraphicsScene::items() const { Q_D(const QGraphicsScene); - const_cast<QGraphicsScenePrivate *>(d)->purgeRemovedItems(); + return d->index->items(Qt::AscendingOrder); +} - // If freeItemIndexes is empty, we know there are no holes in indexedItems and - // unindexedItems. - if (d->freeItemIndexes.isEmpty()) { - if (d->unindexedItems.isEmpty()) - return d->indexedItems; - return d->indexedItems + d->unindexedItems; - } +/*! + Returns an ordered list of all items on the scene. \a order decides the + sorting. - // Rebuild the list of items to avoid holes. ### We could also just - // compress the item lists at this point. - QList<QGraphicsItem *> itemList; - foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) { - if (item) - itemList << item; - } - return itemList; + \sa addItem(), removeItem() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(Qt::SortOrder order) const +{ + Q_D(const QGraphicsScene); + return d->index->items(order); } /*! + \obsolete + Returns all visible items at position \a pos in the scene. The items are - listed in descending Z order (i.e., the first item in the list is the + listed in descending stacking order (i.e., the first item in the list is the top-most item, and the last item is the bottom-most item). + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. + \sa itemAt() */ QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const { Q_D(const QGraphicsScene); - return d->items_helper(pos); + return d->index->items(pos, Qt::IntersectsItemShape, Qt::AscendingOrder); } - /*! - \fn QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const - \overload + \obsolete Returns all visible items that, depending on \a mode, are either inside or intersect with the specified \a rectangle. @@ -2486,23 +1688,47 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const The default value for \a mode is Qt::IntersectsItemShape; all items whose exact shape intersects with or is contained by \a rectangle are returned. + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. + \sa itemAt() */ -QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode) const +QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->items_helper(rect, mode, Qt::AscendingOrder); + return d->index->items(rectangle, mode, Qt::AscendingOrder); } /*! \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode) const + \obsolete \since 4.3 This convenience function is equivalent to calling items(QRectF(\a x, \a y, \a w, \a h), \a mode). + + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. */ /*! + \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, + Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const \overload + \since 4.6 + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the rectangle defined by \a x, \a y, \a w and \a h, in a list + sorted using \a order. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. +*/ + +/*! + \overload + \obsolete Returns all visible items that, depending on \a mode, are either inside or intersect with the polygon \a polygon. @@ -2510,16 +1736,21 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelecti The default value for \a mode is Qt::IntersectsItemShape; all items whose exact shape intersects with or is contained by \a polygon are returned. + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. + \sa itemAt() */ QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->items_helper(polygon, mode, Qt::AscendingOrder); + return d->index->items(polygon, mode, Qt::AscendingOrder); } /*! \overload + \obsolete Returns all visible items that, depending on \a path, are either inside or intersect with the path \a path. @@ -2527,12 +1758,103 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemS The default value for \a mode is Qt::IntersectsItemShape; all items whose exact shape intersects with or is contained by \a path are returned. + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. + \sa itemAt() */ QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->items_helper(path, mode, Qt::AscendingOrder); + return d->index->items(path, mode, Qt::AscendingOrder); +} + +/*! + \since 4.6 + + Returns all visible items that, depending on \a mode, are at the specified \a pos + in a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with \a pos are returned. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(pos, mode, order, deviceTransform); +} + +/*! + \overload + \since 4.6 + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a rect and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a rect are returned. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(rect, mode, order, deviceTransform); +} + +/*! + \overload + \since 4.6 + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a polygon and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a polygon are returned. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(polygon, mode, order, deviceTransform); +} + +/*! + \overload + \since 4.6 + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a path and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a path are returned. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(path, mode, order, deviceTransform); } /*! @@ -2555,34 +1877,77 @@ QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item, return QList<QGraphicsItem *>(); } + // Does not support ItemIgnoresTransformations. QList<QGraphicsItem *> tmp; - foreach (QGraphicsItem *itemInVicinity, d->estimateItemsInRect(item->sceneBoundingRect())) { + foreach (QGraphicsItem *itemInVicinity, d->index->estimateItems(item->sceneBoundingRect(), Qt::AscendingOrder)) { if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode)) tmp << itemInVicinity; } - d->sortItems(&tmp, Qt::AscendingOrder, d->sortCacheEnabled); return tmp; } /*! - \fn QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const + \overload + \obsolete Returns the topmost visible item at the specified \a position, or 0 if there are no items at this position. \note The topmost item is the one with the highest Z-value. + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. + \sa items(), collidingItems(), QGraphicsItem::setZValue() */ -QGraphicsItem *QGraphicsScene::itemAt(const QPointF &pos) const +QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const +{ + QList<QGraphicsItem *> itemsAtPoint = items(position); + return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first(); +} + +/*! + \since 4.6 + + Returns the topmost visible item at the specified \a position, or 0 + if there are no items at this position. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + \note The topmost item is the one with the highest Z-value. + + \sa items(), collidingItems(), QGraphicsItem::setZValue() + */ +QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position, const QTransform &deviceTransform) const { - QList<QGraphicsItem *> itemsAtPoint = items(pos); + QList<QGraphicsItem *> itemsAtPoint = items(position, Qt::IntersectsItemShape, + Qt::AscendingOrder, deviceTransform); return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first(); } /*! + \fn QGraphicsScene::itemAt(qreal x, qreal y, const QTransform &deviceTransform) const + \overload + \since 4.6 + + Returns the topmost item at the position specified by (\a x, \a + y), or 0 if there are no items at this position. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + This convenience function is equivalent to calling \c + {itemAt(QPointF(x, y), deviceTransform)}. + + \note The topmost item is the one with the highest Z-value. +*/ + +/*! \fn QGraphicsScene::itemAt(qreal x, qreal y) const \overload + \obsolete Returns the topmost item at the position specified by (\a x, \a y), or 0 if there are no items at this position. @@ -2590,6 +1955,10 @@ QGraphicsItem *QGraphicsScene::itemAt(const QPointF &pos) const This convenience function is equivalent to calling \c {itemAt(QPointF(x, y))}. + This function is deprecated and returns incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. + \note The topmost item is the one with the highest Z-value. */ @@ -2630,21 +1999,42 @@ QPainterPath QGraphicsScene::selectionArea() const } /*! + \since 4.6 + Sets the selection area to \a path. All items within this area are immediately selected, and all items outside are unselected. You can get the list of all selected items by calling selectedItems(). + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + For an item to be selected, it must be marked as \e selectable (QGraphicsItem::ItemIsSelectable). \sa clearSelection(), selectionArea() */ +void QGraphicsScene::setSelectionArea(const QPainterPath &path, const QTransform &deviceTransform) +{ + setSelectionArea(path, Qt::IntersectsItemShape, deviceTransform); +} + +/*! + \obsolete + \overload + + Sets the selection area to \a path. + + This function is deprecated and leads to incorrect results if the scene + contains items that ignore transformations. Use the overload that takes + a QTransform instead. +*/ void QGraphicsScene::setSelectionArea(const QPainterPath &path) { - setSelectionArea(path, Qt::IntersectsItemShape); + setSelectionArea(path, Qt::IntersectsItemShape, QTransform()); } /*! + \obsolete \overload \since 4.3 @@ -2655,6 +2045,24 @@ void QGraphicsScene::setSelectionArea(const QPainterPath &path) */ void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode) { + setSelectionArea(path, mode, QTransform()); +} + +/*! + \overload + \since 4.6 + + Sets the selection area to \a path using \a mode to determine if items are + included in the selection area. + + \a deviceTransform is the transformation that applies to the view, and needs to + be provided if the scene contains items that ignore transformations. + + \sa clearSelection(), selectionArea() +*/ +void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) +{ Q_D(QGraphicsScene); // Note: with boolean path operations, we can improve performance here @@ -2670,7 +2078,7 @@ void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectio bool changed = false; // Set all items in path to selected. - foreach (QGraphicsItem *item, items(path, mode)) { + foreach (QGraphicsItem *item, items(path, mode, Qt::AscendingOrder, deviceTransform)) { if (item->flags() & QGraphicsItem::ItemIsSelectable) { if (!item->isSelected()) changed = true; @@ -2727,27 +2135,16 @@ void QGraphicsScene::clearSelection() void QGraphicsScene::clear() { Q_D(QGraphicsScene); - // Recursive descent delete - for (int i = 0; i < d->indexedItems.size(); ++i) { - if (QGraphicsItem *item = d->indexedItems.at(i)) { - if (!item->parentItem()) - delete item; - } - } - QList<QGraphicsItem *> unindexedParents; - for (int i = 0; i < d->unindexedItems.size(); ++i) { - QGraphicsItem *item = d->unindexedItems.at(i); - if (!item->parentItem()) - unindexedParents << item; - } - d->unindexedItems.clear(); - qDeleteAll(unindexedParents); - - d->indexedItems.clear(); - d->freeItemIndexes.clear(); + // NB! We have to clear the index before deleting items; otherwise the + // index might try to access dangling item pointers. + d->index->clear(); + const QList<QGraphicsItem *> items = d->topLevelItems; + qDeleteAll(items); + Q_ASSERT(d->topLevelItems.isEmpty()); d->lastItemCount = 0; - d->bspTree.clear(); - d->largestUntransformableItem = QRectF(); + d->allItemsIgnoreHoverEvents = true; + d->allItemsUseDefaultCursor = true; + d->allItemsIgnoreTouchEvents = true; } /*! @@ -2851,7 +2248,6 @@ void QGraphicsScene::addItem(QGraphicsItem *item) qWarning("QGraphicsScene::addItem: item has already been added to this scene"); return; } - // Remove this item from its existing scene if (QGraphicsScene *oldScene = item->scene()) oldScene->removeItem(item); @@ -2867,14 +2263,6 @@ void QGraphicsScene::addItem(QGraphicsItem *item) return; } - // Prevent reusing a recently deleted pointer: purge all removed items - // from our lists. - d->purgeRemovedItems(); - - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d->invalidateSortCache(); - // Detach this item from its parent if the parent's scene is different // from this scene. if (QGraphicsItem *itemParent = item->parentItem()) { @@ -2885,30 +2273,42 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Add the item to this scene item->d_func()->scene = targetScene; - // Indexing requires sceneBoundingRect(), but because \a item might - // not be completely constructed at this point, we need to store it in - // a temporary list and schedule an indexing for later. - d->unindexedItems << item; - item->d_func()->index = -1; - d->startIndexTimer(); + // Add the item in the index + d->index->addItem(item); - // Update the scene's sort cache settings. - item->d_ptr->globalStackingOrder = -1; - d->invalidateSortCache(); + // Add to list of toplevels if this item is a toplevel. + if (!item->d_ptr->parent) + d->registerTopLevelItem(item); // Add to list of items that require an update. We cannot assume that the // item is fully constructed, so calling item->update() can lead to a pure // virtual function call to boundingRect(). - if (!d->updateAll) { - if (d->pendingUpdateItems.isEmpty()) - QMetaObject::invokeMethod(this, "_q_updateLater", Qt::QueuedConnection); - d->pendingUpdateItems << item; - } + d->markDirty(item); + d->dirtyGrowingItemsBoundingRect = true; // Disable selectionChanged() for individual items ++d->selectionChanging; int oldSelectedItemSize = d->selectedItems.size(); + // Enable mouse tracking if the item accepts hover events or has a cursor set. + if (d->allItemsIgnoreHoverEvents && d->itemAcceptsHoverEvents_helper(item)) { + d->allItemsIgnoreHoverEvents = false; + d->enableMouseTrackingOnViews(); + } +#ifndef QT_NO_CURSOR + if (d->allItemsUseDefaultCursor && item->hasCursor()) { + d->allItemsUseDefaultCursor = false; + if (d->allItemsIgnoreHoverEvents) // already enabled otherwise + d->enableMouseTrackingOnViews(); + } +#endif //QT_NO_CURSOR + + // Enable touch events if the item accepts touch events. + if (d->allItemsIgnoreTouchEvents && item->acceptTouchEvents()) { + d->allItemsIgnoreTouchEvents = false; + d->enableTouchEventsOnViews(); + } + // Update selection lists if (item->isSelected()) d->selectedItems << item; @@ -2955,6 +2355,17 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Deliver post-change notification item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant); + + // Auto-activate the first inactive window if the scene is active. + if (d->activationRefCount > 0 && !d->activeWindow && item->isWindow()) + setActiveWindow(static_cast<QGraphicsWidget *>(item)); + + // Ensure that newly added items that have subfocus set, gain + // focus automatically if there isn't a focus item already. + if (!d->focusItem && item->focusItem()) + item->focusItem()->setFocus(); + + d->updateInputMethodSensitivityInViews(); } /*! @@ -3226,109 +2637,12 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) return; } - // If the item has focus, remove it (and any focusWidget reference). - item->clearFocus(); - - // Clear its background - item->update(); - - // Note: This will access item's sceneBoundingRect(), which (as this is - // C++) is why we cannot call removeItem() from QGraphicsItem's - // destructor. - d->removeFromIndex(item); - - if (item == d->tabFocusFirst) { - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); - widget->d_func()->fixFocusChainBeforeReparenting(0, 0); - } - // Set the item's scene ptr to 0. - item->d_func()->scene = 0; - - // Detach the item from its parent. - if (QGraphicsItem *parentItem = item->parentItem()) { - if (parentItem->scene()) { - Q_ASSERT_X(parentItem->scene() == this, "QGraphicsScene::removeItem", - "Parent item's scene is different from this item's scene"); - item->setParentItem(0); - } - } - - // Remove from our item lists. - int index = item->d_func()->index; - if (index != -1) { - d->freeItemIndexes << index; - d->indexedItems[index] = 0; - } else { - d->unindexedItems.removeAll(item); - } - - // Remove from scene transform cache - int transformIndex = item->d_func()->sceneTransformIndex; - if (transformIndex != -1) { - d->validTransforms.setBit(transformIndex, 0); - d->freeSceneTransformSlots.append(transformIndex); - item->d_func()->sceneTransformIndex = -1; - } - - if (item == d->focusItem) - d->focusItem = 0; - if (item == d->lastFocusItem) - d->lastFocusItem = 0; - if (item == d->activeWindow) { - // ### deactivate... - d->activeWindow = 0; - } - - // Disable selectionChanged() for individual items - ++d->selectionChanging; - int oldSelectedItemsSize = d->selectedItems.size(); - - // Update selected & hovered item bookkeeping - d->selectedItems.remove(item); - d->hoverItems.removeAll(item); - d->pendingUpdateItems.removeAll(item); - d->cachedItemsUnderMouse.removeAll(item); - d->unpolishedItems.removeAll(item); - d->dirtyItems.removeAll(item); - - //We remove all references of item from the sceneEventFilter arrays - QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = d->sceneEventFilters.begin(); - while (iterator != d->sceneEventFilters.end()) { - if (iterator.value() == item || iterator.key() == item) - iterator = d->sceneEventFilters.erase(iterator); - else - ++iterator; - } - - - //Ensure dirty flag have the correct default value so the next time it will be added it will receive updates - item->d_func()->dirty = 0; - item->d_func()->dirtyChildren = 0; - - // Remove all children recursively - foreach (QGraphicsItem *child, item->children()) - removeItem(child); - - // Reset the mouse grabber and focus item data. - if (d->mouseGrabberItems.contains(item)) - d->ungrabMouse(item); - - // Reset the keyboard grabber - if (d->keyboardGrabberItems.contains(item)) - item->ungrabKeyboard(); - - // Reset the last mouse grabber item - if (item == d->lastMouseGrabberItem) - d->lastMouseGrabberItem = 0; - - // Reenable selectionChanged() for individual items - --d->selectionChanging; - - if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemsSize) - emit selectionChanged(); + d->removeItemHelper(item); // Deliver post-change notification item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant); + + d->updateInputMethodSensitivityInViews(); } /*! @@ -3365,36 +2679,10 @@ QGraphicsItem *QGraphicsScene::focusItem() const void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason) { Q_D(QGraphicsScene); - if (item == d->focusItem) - return; - if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable) - || !item->isVisible() || !item->isEnabled())) { - item = 0; - } - - if (item) { - setFocus(focusReason); - if (item == d->focusItem) - return; - } - - if (d->focusItem) { - QFocusEvent event(QEvent::FocusOut, focusReason); - d->lastFocusItem = d->focusItem; - d->focusItem = 0; - d->sendEvent(d->lastFocusItem, &event); - } - - if (item) { - if (item->isWidget()) { - // Update focus child chain. - static_cast<QGraphicsWidget *>(item)->d_func()->setFocusWidget(); - } - - d->focusItem = item; - QFocusEvent event(QEvent::FocusIn, focusReason); - d->sendEvent(item, &event); - } + if (item) + item->setFocus(focusReason); + else + d->setFocusItemHelper(item, focusReason); } /*! @@ -3449,13 +2737,17 @@ void QGraphicsScene::clearFocus() /*! \property QGraphicsScene::stickyFocus - \brief whether or not clicking the scene will clear focus + \brief whether clicking into the scene background will clear focus + + \since 4.6 - If this property is false (the default), then clicking on the scene - background or on an item that does not accept focus, will clear - focus. Otherwise, focus will remain unchanged. + In a QGraphicsScene with stickyFocus set to true, focus will remain + unchanged when the user clicks into the scene background or on an item + that does not accept focus. Otherwise, focus will be cleared. - The focus change happens in response to a mouse press. You can reimplement + By default, this property is false. + + Focus changes in response to a mouse press. You can reimplement mousePressEvent() in a subclass of QGraphicsScene to toggle this property based on where the user has clicked. @@ -3578,7 +2870,7 @@ void QGraphicsScene::setForegroundBrush(const QBrush &brush) QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const { Q_D(const QGraphicsScene); - if (!d->focusItem) + if (!d->focusItem || !(d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) return QVariant(); const QTransform matrix = d->focusItem->sceneTransform(); QVariant value = d->focusItem->inputMethodQuery(query); @@ -3607,14 +2899,14 @@ void QGraphicsScene::update(const QRectF &rect) // Check if anyone's connected; if not, we can send updates directly to // the views. Otherwise or if there are no views, use old behavior. - bool directUpdates = !(d->connectedSignals & d->changedSignalMask) && !d->views.isEmpty(); + bool directUpdates = !(d->connectedSignals[0] & d->changedSignalMask) && !d->views.isEmpty(); if (rect.isNull()) { d->updateAll = true; d->updatedRects.clear(); if (directUpdates) { // Update all views. for (int i = 0; i < d->views.size(); ++i) - d->views.at(i)->d_func()->updateAll(); + d->views.at(i)->d_func()->fullUpdatePending = true; } } else { if (directUpdates) { @@ -3628,7 +2920,7 @@ void QGraphicsScene::update(const QRectF &rect) } } - if (!directUpdates && !d->calledEmitUpdated) { + if (!d->calledEmitUpdated) { d->calledEmitUpdated = true; QMetaObject::invokeMethod(this, "_q_emitUpdated", Qt::QueuedConnection); } @@ -3739,11 +3031,16 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: // Reset the under-mouse list to ensure that this event gets fresh // item-under-mouse data. Be careful about this list; if people delete // items from inside event handlers, this list can quickly end up // having stale pointers in it. We need to clear it before dispatching // events that use it. + // ### this should only be cleared if we received a new mouse move event, + // which relies on us fixing the replay mechanism in QGraphicsView. d->cachedItemsUnderMouse.clear(); default: break; @@ -3890,16 +3187,11 @@ bool QGraphicsScene::event(QEvent *event) // geometries that do not have an explicit style set. update(); break; - case QEvent::Timer: - if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { - if (d->restartIndexTimer) { - d->restartIndexTimer = false; - } else { - // this call will kill the timer - d->_q_updateIndex(); - } - } - // Fallthrough intended - support timers in subclasses. + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + d->touchEventHandler(static_cast<QTouchEvent *>(event)); + break; default: return QObject::event(event); } @@ -3919,10 +3211,10 @@ bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event) switch (event->type()) { case QEvent::ApplicationPaletteChange: - qApp->postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); break; case QEvent::ApplicationFontChange: - qApp->postEvent(this, new QEvent(QEvent::ApplicationFontChange)); + QApplication::postEvent(this, new QEvent(QEvent::ApplicationFontChange)); break; default: break; @@ -4188,7 +3480,7 @@ void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent) text = toolTipItem->toolTip(); point = helpEvent->screenPos(); } - QToolTip::showText(point, text); + QToolTip::showText(point, text, helpEvent->widget()); helpEvent->setAccepted(!text.isEmpty()); #endif } @@ -4209,6 +3501,9 @@ bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *i */ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent) { + if (allItemsIgnoreHoverEvents) + return false; + // Find the first item that accepts hover events, reusing earlier // calculated data is possible. if (cachedItemsUnderMouse.isEmpty()) { @@ -4284,8 +3579,7 @@ void QGraphicsScenePrivate::leaveScene() { Q_Q(QGraphicsScene); #ifndef QT_NO_TOOLTIP - // Remove any tooltips - QToolTip::showText(QPoint(), QString()); + QToolTip::hideText(); #endif // Send HoverLeave events to all existing hover items, topmost first. QGraphicsView *senderWidget = qobject_cast<QGraphicsView *>(q->sender()); @@ -4388,6 +3682,13 @@ void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent) void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { Q_D(QGraphicsScene); + if (d->mouseGrabberItems.isEmpty()) { + // Dispatch hover events + QGraphicsSceneHoverEvent hover; + _q_hoverFromMouseEvent(&hover, mouseEvent); + d->dispatchHoverEvent(&hover); + } + d->mousePressEventHandler(mouseEvent); } @@ -4528,16 +3829,16 @@ void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) subclass to receive input method events for the scene. The default implementation forwards the event to the focusItem(). - If no item currently has focus, this function does nothing. + If no item currently has focus or the current focus item does not + accept input methods, this function does nothing. \sa QGraphicsItem::inputMethodEvent() */ void QGraphicsScene::inputMethodEvent(QInputMethodEvent *event) { Q_D(QGraphicsScene); - if (!d->focusItem) - return; - d->sendEvent(d->focusItem, event); + if (d->focusItem && (d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) + d->sendEvent(d->focusItem, event); } /*! @@ -4608,7 +3909,7 @@ static void _q_paintItem(QGraphicsItem *item, QPainter *painter, ? proxy->widget()->windowOpacity() : 1.0; const qreal oldPainterOpacity = painter->opacity(); - if (qFuzzyCompare(windowOpacity + 1, qreal(1.0))) + if (qFuzzyIsNull(windowOpacity)) return; // Set new painter opacity. if (windowOpacity < 1.0) @@ -4717,8 +4018,9 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte return; // Fetch the off-screen transparent buffer and exposed area info. - QString pixmapKey; + QPixmapCache::Key pixmapKey; QPixmap pix; + bool pixmapFound; QGraphicsItemCache *itemCache = itemd->extraItemCache(); if (cacheMode == QGraphicsItem::ItemCoordinateCache) { if (itemCache->boundingRect != brect.toRect()) { @@ -4728,17 +4030,11 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } pixmapKey = itemCache->key; } else { - if ((pixmapKey = itemCache->deviceData.value(widget).key).isEmpty()) { - pixmapKey.sprintf("qgv-%p-%p", item, widget); - QGraphicsItemCache::DeviceData data; - data.key = pixmapKey; - itemCache->deviceData.insert(widget, data); - } + pixmapKey = itemCache->deviceData.value(widget).key; } // Find pixmap in cache. - if (!itemCache->allExposed) - QPixmapCache::find(pixmapKey, pix); + pixmapFound = QPixmapCache::find(pixmapKey, &pix); // Render using item coordinate cache mode. if (cacheMode == QGraphicsItem::ItemCoordinateCache) { @@ -4763,6 +4059,12 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte // Redraw any newly exposed areas. if (itemCache->allExposed || !itemCache->exposed.isEmpty()) { + + //We know that we will modify the pixmap, removing it from the cache + //will detach the one we have and avoid a deep copy + if (pixmapFound) + QPixmapCache::remove(pixmapKey); + // Fit the item's bounding rect into the pixmap's coordinates. QTransform itemToPixmap; if (fixedCacheSize) { @@ -4773,7 +4075,7 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte // Generate the item's exposedRect and map its list of expose // rects to device coordinates. - QStyleOptionGraphicsItem cacheOption = *option; + styleOptionTmp = *option; QRegion pixmapExposed; QRectF exposedRect; if (!itemCache->allExposed) { @@ -4785,14 +4087,14 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } else { exposedRect = brect; } - cacheOption.exposedRect = exposedRect; + styleOptionTmp.exposedRect = exposedRect; // Render. _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), - &cacheOption, painterStateProtection); + &styleOptionTmp, painterStateProtection); - // Reinsert this pixmap into the cache. - QPixmapCache::insert(pixmapKey, pix); + // insert this pixmap into the cache. + itemCache->key = QPixmapCache::insert(pix); // Reset expose data. itemCache->allExposed = false; @@ -4858,7 +4160,7 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte bool allowPartialCacheExposure = !viewRect.contains(deviceRect); #else // Only if deviceRect is 20% taller or wider than the desktop. - QRect desktopRect = qApp->desktop()->availableGeometry(widget); + QRect desktopRect = QApplication::desktop()->availableGeometry(widget); bool allowPartialCacheExposure = (desktopRect.width() * 1.2 < deviceRect.width() || desktopRect.height() * 1.2 < deviceRect.height()); #endif @@ -4919,6 +4221,11 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte // Check for newly invalidated areas. if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) { + //We know that we will modify the pixmap, removing it from the cache + //will detach the one we have and avoid a deep copy + if (pixmapFound) + QPixmapCache::remove(pixmapKey); + // Construct an item-to-pixmap transform. QPointF p = deviceRect.topLeft(); QTransform itemToPixmap = painter->worldTransform(); @@ -4945,12 +4252,12 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte foreach (QRect r, scrollExposure.rects()) br |= pixmapToItem.mapRect(r); } - QStyleOptionGraphicsItem cacheOption = *option; - cacheOption.exposedRect = br.adjusted(-1, -1, 1, 1); + styleOptionTmp = *option; + styleOptionTmp.exposedRect = br.adjusted(-1, -1, 1, 1); // Render the exposed areas. _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), - &cacheOption, painterStateProtection); + &styleOptionTmp, painterStateProtection); // Reset expose data. pixModified = true; @@ -4959,8 +4266,8 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } if (pixModified) { - // Reinsert this pixmap into the cache - QPixmapCache::insert(pixmapKey, pix); + // Insert this pixmap into the cache. + deviceData->key = QPixmapCache::insert(pix); } // Redraw the exposed area using an untransformed painter. This @@ -4979,12 +4286,448 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } } +void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const viewTransform, + QRegion *exposedRegion, QWidget *widget) +{ + QRectF exposedSceneRect; + if (exposedRegion && indexMethod != QGraphicsScene::NoIndex) { + exposedSceneRect = exposedRegion->boundingRect().adjusted(-1, -1, 1, 1); + if (viewTransform) + exposedSceneRect = viewTransform->inverted().mapRect(exposedSceneRect); + } + const QList<QGraphicsItem *> tli = index->estimateTopLevelItems(exposedSceneRect, Qt::DescendingOrder); + for (int i = 0; i < tli.size(); ++i) + drawSubtreeRecursive(tli.at(i), painter, viewTransform, exposedRegion, widget); +} + +void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, + const QTransform *const viewTransform, + QRegion *exposedRegion, QWidget *widget, + qreal parentOpacity) +{ + Q_ASSERT(item); + + if (!item->d_ptr->visible) + return; + + const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + if (!itemHasContents && !itemHasChildren) + return; // Item has neither contents nor children!(?) + + const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity); + const bool itemIsFullyTransparent = (opacity < 0.0001); + if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) + return; + + QTransform transform(Qt::Uninitialized); + QTransform *transformPtr = 0; + bool translateOnlyTransform = false; +#define ENSURE_TRANSFORM_PTR \ + if (!transformPtr) { \ + Q_ASSERT(!itemIsUntransformable); \ + if (viewTransform) { \ + transform = item->d_ptr->sceneTransform; \ + transform *= *viewTransform; \ + transformPtr = &transform; \ + } else { \ + transformPtr = &item->d_ptr->sceneTransform; \ + translateOnlyTransform = item->d_ptr->sceneTransformTranslateOnly; \ + } \ + } + + // Update the item's scene transform if the item is transformable; + // otherwise calculate the full transform, + bool wasDirtyParentSceneTransform = false; + const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable(); + if (itemIsUntransformable) { + transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform()); + transformPtr = &transform; + } else if (item->d_ptr->dirtySceneTransform) { + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); + wasDirtyParentSceneTransform = true; + } + + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + bool drawItem = itemHasContents && !itemIsFullyTransparent; + if (drawItem) { + const QRectF brect = adjustedItemBoundingRect(item); + ENSURE_TRANSFORM_PTR + QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect() + : transformPtr->mapRect(brect).toRect(); + item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); + viewBoundingRect.adjust(-1, -1, 1, 1); + drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty(); + if (!drawItem) { + if (!itemHasChildren) + return; + if (itemClipsChildrenToShape) { + if (wasDirtyParentSceneTransform) + item->d_ptr->invalidateChildrenSceneTransform(); + return; + } + } + } // else we know for sure this item has children we must process. + + int i = 0; + if (itemHasChildren) { + item->d_ptr->ensureSortedChildren(); + + if (itemClipsChildrenToShape) { + painter->save(); + ENSURE_TRANSFORM_PTR + painter->setWorldTransform(*transformPtr); + painter->setClipPath(item->shape(), Qt::IntersectClip); + } + + // Draw children behind + for (i = 0; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent)) + break; + if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) + continue; + drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity); + } + } + + // Draw item + if (drawItem) { + Q_ASSERT(!itemIsFullyTransparent); + Q_ASSERT(itemHasContents); + ENSURE_TRANSFORM_PTR + item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion + ? *exposedRegion : QRegion(), exposedRegion == 0); + + const bool itemClipsToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsToShape; + const bool savePainter = itemClipsToShape || painterStateProtection; + if (savePainter) + painter->save(); + + if (!itemHasChildren || !itemClipsChildrenToShape) + painter->setWorldTransform(*transformPtr); + + if (itemClipsToShape) + painter->setClipPath(item->shape(), Qt::IntersectClip); + painter->setOpacity(opacity); + + if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget) + item->paint(painter, &styleOptionTmp, widget); + else + drawItemHelper(item, painter, &styleOptionTmp, widget, painterStateProtection); + + if (savePainter) + painter->restore(); + } + + // Draw children in front + if (itemHasChildren) { + for (; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) + continue; + drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity); + } + } + + // Restore child clip + if (itemHasChildren && itemClipsChildrenToShape) + painter->restore(); +} + +void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, bool invalidateChildren, + bool maybeDirtyClipPath, bool force, bool ignoreOpacity, + bool removingItemFromScene) +{ + Q_ASSERT(item); + if (updateAll) + return; + + if (item->d_ptr->discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, + /*ignoreVisibleBit=*/force, + /*ignoreDirtyBit=*/removingItemFromScene || invalidateChildren, + /*ignoreOpacity=*/ignoreOpacity)) { + if (item->d_ptr->dirty) { + // The item is already marked as dirty and will be processed later. However, + // we have to make sure ignoreVisible and ignoreOpacity are set properly; + // otherwise things like: item->update(); item->hide() (force is now true) + // won't work as expected. + if (force) + item->d_ptr->ignoreVisible = 1; + if (ignoreOpacity) + item->d_ptr->ignoreOpacity = 1; + } + return; + } + + const bool fullItemUpdate = rect.isNull(); + if (!fullItemUpdate && rect.isEmpty()) + return; + + if (!processDirtyItemsEmitted) { + QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection); + processDirtyItemsEmitted = true; + } + + if (removingItemFromScene) { + // Note that this function can be called from the item's destructor, so + // do NOT call any virtual functions on it within this block. + if ((connectedSignals[0] & changedSignalMask) || views.isEmpty()) { + // This block of code is kept for compatibility. Since 4.5, by default + // QGraphicsView does not connect the signal and we use the below + // method of delivering updates. + q_func()->update(); + return; + } + + for (int i = 0; i < views.size(); ++i) { + QGraphicsViewPrivate *viewPrivate = views.at(i)->d_func(); + QRect rect = item->d_ptr->paintedViewBoundingRects.value(viewPrivate->viewport); + rect.translate(viewPrivate->dirtyScrollOffset); + viewPrivate->updateRect(rect); + } + return; + } + + bool hasNoContents = item->d_ptr->flags & QGraphicsItem::ItemHasNoContents; + if (!hasNoContents) { + item->d_ptr->dirty = 1; + if (fullItemUpdate) + item->d_ptr->fullUpdatePending = 1; + else if (!item->d_ptr->fullUpdatePending) + item->d_ptr->needsRepaint |= rect; + } + + if (invalidateChildren) { + item->d_ptr->allChildrenDirty = 1; + item->d_ptr->dirtyChildren = 1; + } + + if (force) + item->d_ptr->ignoreVisible = 1; + if (ignoreOpacity) + item->d_ptr->ignoreOpacity = 1; + + QGraphicsItem *p = item->d_ptr->parent; + while (p && !p->d_ptr->dirtyChildren) { + p->d_ptr->dirtyChildren = 1; + p = p->d_ptr->parent; + } +} + +static inline bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item, + const QRectF &rect, bool itemIsUntransformable) +{ + Q_ASSERT(view); + Q_ASSERT(item); + + QGraphicsItem *itemq = static_cast<QGraphicsItem *>(item->q_ptr); + QGraphicsView *viewq = static_cast<QGraphicsView *>(view->q_ptr); + + if (itemIsUntransformable) { + const QTransform xform = itemq->deviceTransform(viewq->viewportTransform()); + if (!item->hasBoundingRegionGranularity) + return view->updateRect(xform.mapRect(rect).toRect()); + return view->updateRegion(xform.map(QRegion(rect.toRect()))); + } + + if (item->sceneTransformTranslateOnly && view->identityMatrix) { + const qreal dx = item->sceneTransform.dx(); + const qreal dy = item->sceneTransform.dy(); + if (!item->hasBoundingRegionGranularity) { + QRectF r(rect); + r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll()); + return view->updateRect(r.toRect()); + } + QRegion r(rect.toRect()); + r.translate(qRound(dx) - view->horizontalScroll(), qRound(dy) - view->verticalScroll()); + return view->updateRegion(r); + } + + if (!viewq->isTransformed()) { + if (!item->hasBoundingRegionGranularity) + return view->updateRect(item->sceneTransform.mapRect(rect).toRect()); + return view->updateRegion(item->sceneTransform.map(QRegion(rect.toRect()))); + } + + QTransform xform = item->sceneTransform; + xform *= viewq->viewportTransform(); + if (!item->hasBoundingRegionGranularity) + return view->updateRect(xform.mapRect(rect).toRect()); + return view->updateRegion(xform.map(QRegion(rect.toRect()))); +} + +void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren, + qreal parentOpacity) +{ + Q_Q(QGraphicsScene); + Q_ASSERT(item); + Q_ASSERT(!updateAll); + + if (!item->d_ptr->dirty && !item->d_ptr->dirtyChildren) { + resetDirtyItem(item); + return; + } + + const bool itemIsHidden = !item->d_ptr->ignoreVisible && !item->d_ptr->visible; + if (itemIsHidden) { + resetDirtyItem(item, /*recursive=*/true); + return; + } + + const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + if (!itemHasContents && !itemHasChildren) { + resetDirtyItem(item); + return; // Item has neither contents nor children!(?) + } + + const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity); + const bool itemIsFullyTransparent = !item->d_ptr->ignoreOpacity && opacity < 0.0001; + if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) { + resetDirtyItem(item, /*recursive=*/itemHasChildren); + return; + } + + bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform; + const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable(); + if (wasDirtyParentSceneTransform && !itemIsUntransformable) { + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); + } + + const bool wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint; + if (itemIsFullyTransparent || !itemHasContents || dirtyAncestorContainsChildren) { + // Make sure we don't process invisible items or items with no content. + item->d_ptr->dirty = 0; + item->d_ptr->fullUpdatePending = 0; + // Might have a dirty view bounding rect otherwise. + if (itemIsFullyTransparent || !itemHasContents) + item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0; + } + + if (!hasSceneRect && item->d_ptr->geometryChanged && item->d_ptr->visible) { + // Update growingItemsBoundingRect. + if (item->d_ptr->sceneTransformTranslateOnly) { + growingItemsBoundingRect |= item->boundingRect().translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy()); + } else { + growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect()); + } + } + + // Process item. + if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) { + const bool useCompatUpdate = views.isEmpty() || (connectedSignals[0] & changedSignalMask); + const QRectF itemBoundingRect = adjustedItemBoundingRect(item); + + if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) { + // This block of code is kept for compatibility. Since 4.5, by default + // QGraphicsView does not connect the signal and we use the below + // method of delivering updates. + if (item->d_ptr->sceneTransformTranslateOnly) { + q->update(itemBoundingRect.translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy())); + } else { + q->update(item->d_ptr->sceneTransform.mapRect(itemBoundingRect)); + } + } else { + QRectF dirtyRect; + bool uninitializedDirtyRect = true; + + for (int j = 0; j < views.size(); ++j) { + QGraphicsView *view = views.at(j); + QGraphicsViewPrivate *viewPrivate = view->d_func(); + QRect &paintedViewBoundingRect = item->d_ptr->paintedViewBoundingRects[viewPrivate->viewport]; + if (viewPrivate->fullUpdatePending + || viewPrivate->viewportUpdateMode == QGraphicsView::NoViewportUpdate) { + // Okay, if we have a full update pending or no viewport update, this item's + // paintedViewBoundingRect will be updated correctly in the next paintEvent if + // it is inside the viewport, but for now we can pretend that it is outside. + paintedViewBoundingRect = QRect(-1, -1, -1, -1); + continue; + } + + if (item->d_ptr->paintedViewBoundingRectsNeedRepaint && !paintedViewBoundingRect.isEmpty()) { + paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset); + if (!viewPrivate->updateRect(paintedViewBoundingRect)) + paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport. + } + + if (!item->d_ptr->dirty) + continue; + + if (!item->d_ptr->paintedViewBoundingRectsNeedRepaint + && paintedViewBoundingRect.x() == -1 && paintedViewBoundingRect.y() == -1 + && paintedViewBoundingRect.width() == -1 && paintedViewBoundingRect.height() == -1) { + continue; // Outside viewport. + } + + if (uninitializedDirtyRect) { + dirtyRect = itemBoundingRect; + if (!item->d_ptr->fullUpdatePending) { + _q_adjustRect(&item->d_ptr->needsRepaint); + dirtyRect &= item->d_ptr->needsRepaint; + } + uninitializedDirtyRect = false; + } + + if (dirtyRect.isEmpty()) + continue; // Discard updates outside the bounding rect. + + if (!updateHelper(viewPrivate, item->d_ptr, dirtyRect, itemIsUntransformable) + && item->d_ptr->paintedViewBoundingRectsNeedRepaint) { + paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport. + } + } + } + } + + // Process children. + if (itemHasChildren && item->d_ptr->dirtyChildren) { + if (!dirtyAncestorContainsChildren) { + dirtyAncestorContainsChildren = item->d_ptr->fullUpdatePending + && (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + } + const bool allChildrenDirty = item->d_ptr->allChildrenDirty; + const bool parentIgnoresVisible = item->d_ptr->ignoreVisible; + const bool parentIgnoresOpacity = item->d_ptr->ignoreOpacity; + for (int i = 0; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (wasDirtyParentViewBoundingRects) + child->d_ptr->paintedViewBoundingRectsNeedRepaint = 1; + if (parentIgnoresVisible) + child->d_ptr->ignoreVisible = 1; + if (parentIgnoresOpacity) + child->d_ptr->ignoreOpacity = 1; + if (allChildrenDirty) { + child->d_ptr->dirty = 1; + child->d_ptr->fullUpdatePending = 1; + child->d_ptr->dirtyChildren = 1; + child->d_ptr->allChildrenDirty = 1; + } + processDirtyItemsRecursive(child, dirtyAncestorContainsChildren, opacity); + } + } else if (wasDirtyParentSceneTransform) { + item->d_ptr->invalidateChildrenSceneTransform(); + } + + resetDirtyItem(item); +} + /*! Paints the given \a items using the provided \a painter, after the background has been drawn, and before the foreground has been drawn. All painting is done in \e scene coordinates. Before drawing each item, the painter must be transformed using - QGraphicsItem::sceneMatrix(). + QGraphicsItem::sceneTransform(). The \a options parameter is the list of style option objects for each item in \a items. The \a numItems parameter is the number of @@ -5010,111 +4753,29 @@ void QGraphicsScene::drawItems(QPainter *painter, const QStyleOptionGraphicsItem options[], QWidget *widget) { Q_D(QGraphicsScene); - - // Detect if painter state protection is disabled. QTransform viewTransform = painter->worldTransform(); - QVarLengthArray<QGraphicsItem *, 16> childClippers; + Q_UNUSED(options); - for (int i = 0; i < numItems; ++i) { - QGraphicsItem *item = items[i]; - if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { - if (!childClippers.isEmpty()) { - // Item is not clipped to any ancestor: pop all current clippers. - for (int i = 0; i < childClippers.size(); ++i) - painter->restore(); - childClippers.clear(); - } - } else { - // Item is clipped to an ancestor, which may or may not be in our - // child clipper list. Let's start by finding the item's closest - // clipping ancestor. - QGraphicsItem *clipParent = item->parentItem(); - while (clipParent && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)) - clipParent = clipParent->parentItem(); - - // Pop any in-between clippers. If the clipper is unknown, pop - // them all. ### QVarLengthArray::lastIndexOf(). - int index = -1; - for (int n = childClippers.size() - 1; n >= 0; --n) { - if (childClippers[n] == clipParent) { - index = n; - break; - } - } - if (index != -1) { - int toPop = childClippers.size() - index - 1; - if (toPop > 0) { - for (int i = 0; i < toPop; ++i) - painter->restore(); - childClippers.resize(index + 1); - } - } - - // Sanity check - if (!childClippers.isEmpty()) - Q_ASSERT(childClippers[childClippers.size() - 1] == clipParent); - - // If the clipper list is empty at this point, but we're still - // clipped to an ancestor, then we need to build the clip chain - // ourselves. There is only one case that can produce this issue: - // This item is stacked behind an ancestor: - // ItemStacksBehindParent. - if (childClippers.isEmpty()) { - Q_ASSERT(clipParent != 0); - // Build a stack of clippers. - QVarLengthArray<QGraphicsItem *, 16> clippers; - QGraphicsItem *p = clipParent; - do { - if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) - clippers.append(p); - } while ((p = p->parentItem()) && (p->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)); - - // ### This code path can also use the itemTransform - // optimization, but it's hit very rarely. - for (int i = clippers.size() - 1; i >= 0; --i) { - QGraphicsItem *clipper = clippers[i]; - painter->setWorldTransform(clipper->deviceTransform(viewTransform), false); - - childClippers.append(clipper); - painter->save(); - painter->setClipPath(clipper->shape(), Qt::IntersectClip); - } - Q_ASSERT(childClippers[childClippers.size() - 1] == clipParent); - } - } - - // Set up the painter transform - painter->setWorldTransform(item->deviceTransform(viewTransform), false); - - // Save painter - bool saveState = (d->painterStateProtection || (item->flags() & QGraphicsItem::ItemClipsToShape)); - if (saveState) - painter->save(); - - // Set local clip - if (item->flags() & QGraphicsItem::ItemClipsToShape) - painter->setClipPath(item->shape(), Qt::IntersectClip); - - // Setup opacity - painter->setOpacity(item->effectiveOpacity()); - - // Draw the item - d->drawItemHelper(item, painter, &options[i], widget, d->painterStateProtection); - - if (saveState) - painter->restore(); + // Determine view, expose and flags. + QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0; + QRegion *expose = 0; + if (view) + expose = &view->d_func()->exposedRegion; - if (item->flags() & QGraphicsItem::ItemClipsChildrenToShape) { - // Clip descendents to this item's shape, and keep the painter - // saved. - childClippers.append(item); - painter->save(); - painter->setClipPath(item->shape(), Qt::IntersectClip); + // Find all toplevels, they are already sorted. + QList<QGraphicsItem *> topLevelItems; + for (int i = 0; i < numItems; ++i) { + QGraphicsItem *item = items[i]->topLevelItem(); + if (!item->d_ptr->itemDiscovered) { + topLevelItems << item; + item->d_ptr->itemDiscovered = 1; + d->drawSubtreeRecursive(item, painter, &viewTransform, expose, widget); } } - for (int i = 0; i < childClippers.size(); ++i) - painter->restore(); + // Reset discovery bits. + for (int i = 0; i < topLevelItems.size(); ++i) + topLevelItems.at(i)->d_ptr->itemDiscovered = 0; painter->setWorldTransform(viewTransform); } @@ -5226,73 +4887,6 @@ bool QGraphicsScene::focusNextPrevChild(bool next) */ /*! - \internal - - This private function is called by QGraphicsItem, which is a friend of - QGraphicsScene. It is used by QGraphicsScene to record the rectangles that - need updating. It also launches a single-shot timer to ensure that - updated() will be emitted later. - - The \a item parameter is the item that changed, and \a rect is the - area of the item that changed given in item coordinates. -*/ -void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect) -{ - Q_D(QGraphicsScene); - // Deliver the actual update. - if (!d->updateAll) { - if (d->views.isEmpty() || ((d->connectedSignals & d->changedSignalMask) && !item->d_ptr->itemIsUntransformable() - && qFuzzyCompare(item->boundingRegionGranularity(), qreal(0.0)))) { - // This block of code is kept for compatibility. Since 4.5, by default - // QGraphicsView does not connect the signal and we use the below - // method of delivering updates. - update(item->sceneBoundingRect()); - } else { - // ### Remove _q_adjustedRects(). - QRectF boundingRect(adjustedItemBoundingRect(item)); - if (!rect.isNull()) { - QRectF adjustedRect(rect); - _q_adjustRect(&adjustedRect); - boundingRect &= adjustedRect; - } - - // Update each view directly. - for (int i = 0; i < d->views.size(); ++i) - d->views.at(i)->d_func()->itemUpdated(item, boundingRect); - } - } - if (item->d_ptr->dirty) { - d->dirtyItems << item; - d->resetDirtyItemsLater(); - } - - if (!item->isVisible()) - return; // Hiding an item won't effect the largestUntransformableItem/sceneRect. - - // Update d->largestUntransformableItem by mapping this item's bounding - // rect back to the topmost untransformable item's untransformed - // coordinate system (which sort of equals the 1:1 coordinate system of an - // untransformed view). - if (item->d_ptr->itemIsUntransformable()) { - QGraphicsItem *parent = item; - while (parent && (parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations)) - parent = parent->parentItem(); - d->largestUntransformableItem |= item->mapToItem(parent, item->boundingRect()).boundingRect(); - } - - // Only track the automatically growing scene rect if the scene has no - // defined scene rect. - if (!d->hasSceneRect) { - QRectF oldGrowingItemsBoundingRect = d->growingItemsBoundingRect; - QRectF adjustedItemSceneBoundingRect(item->sceneBoundingRect()); - _q_adjustRect(&adjustedItemSceneBoundingRect); - d->growingItemsBoundingRect |= adjustedItemSceneBoundingRect; - if (d->growingItemsBoundingRect != oldGrowingItemsBoundingRect) - emit sceneRectChanged(d->growingItemsBoundingRect); - } -} - -/*! \since 4.4 Returns the scene's style, or the same as QApplication::style() if the @@ -5304,7 +4898,7 @@ QStyle *QGraphicsScene::style() const { Q_D(const QGraphicsScene); // ### This function, and the use of styles in general, is non-reentrant. - return d->style ? d->style : qApp->style(); + return d->style ? d->style : QApplication::style(); } /*! @@ -5381,7 +4975,7 @@ QFont QGraphicsScene::font() const void QGraphicsScene::setFont(const QFont &font) { Q_D(QGraphicsScene); - QFont naturalFont = qApp->font(); + QFont naturalFont = QApplication::font(); naturalFont.resolve(0); QFont resolvedFont = font.resolve(naturalFont); d->setFont_helper(resolvedFont); @@ -5418,7 +5012,7 @@ QPalette QGraphicsScene::palette() const void QGraphicsScene::setPalette(const QPalette &palette) { Q_D(QGraphicsScene); - QPalette naturalPalette = qApp->palette(); + QPalette naturalPalette = QApplication::palette(); naturalPalette.resolve(0); QPalette resolvedPalette = palette.resolve(naturalPalette); d->setPalette_helper(resolvedPalette); @@ -5506,6 +5100,259 @@ void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) } } +/*! + \since 4.6 + + Sends event \a event to item \a item through possible event filters. + + The event is sent only if the item is enabled. + + Returns \c false if the event was filtered or if the item is disabled. + Otherwise returns the value that was returned from the event handler. + + \sa QGraphicsItem::sceneEvent(), QGraphicsItem::sceneEventFilter() +*/ +bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event) +{ + Q_D(QGraphicsScene); + if (!item) { + qWarning("QGraphicsScene::sendEvent: cannot send event to a null item"); + return false; + } + if (item->scene() != this) { + qWarning("QGraphicsScene::sendEvent: item %p's scene (%p)" + " is different from this scene (%p)", + item, item->scene(), this); + return false; + } + return d->sendEvent(item, event); +} + +void QGraphicsScenePrivate::addView(QGraphicsView *view) +{ + views << view; +} + +void QGraphicsScenePrivate::removeView(QGraphicsView *view) +{ + views.removeAll(view); +} + +void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent) +{ + QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + touchPoint.setRect(item->mapFromScene(touchPoint.sceneRect()).boundingRect()); + touchPoint.setStartPos(item->d_ptr->genericMapFromScene(touchPoint.startScenePos(), touchEvent->widget())); + touchPoint.setLastPos(item->d_ptr->genericMapFromScene(touchPoint.lastScenePos(), touchEvent->widget())); + } + touchEvent->setTouchPoints(touchPoints); +} + +int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos) +{ + int closestTouchPointId = -1; + qreal closestDistance = qreal(0.); + foreach (const QTouchEvent::TouchPoint &touchPoint, sceneCurrentTouchPoints) { + qreal distance = QLineF(scenePos, touchPoint.scenePos()).length(); + if (closestTouchPointId == -1|| distance < closestDistance) { + closestTouchPointId = touchPoint.id(); + closestDistance = distance; + } + } + return closestTouchPointId; +} + +void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent) +{ + typedef QPair<Qt::TouchPointStates, QList<QTouchEvent::TouchPoint> > StatesAndTouchPoints; + QHash<QGraphicsItem *, StatesAndTouchPoints> itemsNeedingEvents; + + for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &touchPoint = sceneTouchEvent->touchPoints().at(i); + + // update state + QGraphicsItem *item = 0; + if (touchPoint.state() == Qt::TouchPointPressed) { + if (sceneTouchEvent->deviceType() == QTouchEvent::TouchPad) { + // on touch-pad devices, send all touch points to the same item + item = itemForTouchPointId.isEmpty() + ? 0 + : itemForTouchPointId.constBegin().value(); + } + + if (!item) { + // determine which item this touch point will go to + cachedItemsUnderMouse = itemsAtPosition(touchPoint.screenPos().toPoint(), + touchPoint.scenePos(), + sceneTouchEvent->widget()); + item = cachedItemsUnderMouse.isEmpty() ? 0 : cachedItemsUnderMouse.first(); + } + + if (sceneTouchEvent->deviceType() == QTouchEvent::TouchScreen) { + // on touch-screens, combine this touch point with the closest one we find if it + // is a a direct descendent or ancestor ( + int closestTouchPointId = findClosestTouchPointId(touchPoint.scenePos()); + QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPointId); + if (!item + || (closestItem + && (item->isAncestorOf(closestItem) + || closestItem->isAncestorOf(item)))) { + item = closestItem; + } + } + if (!item) + continue; + + itemForTouchPointId.insert(touchPoint.id(), item); + sceneCurrentTouchPoints.insert(touchPoint.id(), touchPoint); + } else if (touchPoint.state() == Qt::TouchPointReleased) { + item = itemForTouchPointId.take(touchPoint.id()); + if (!item) + continue; + + sceneCurrentTouchPoints.remove(touchPoint.id()); + } else { + item = itemForTouchPointId.value(touchPoint.id()); + if (!item) + continue; + Q_ASSERT(sceneCurrentTouchPoints.contains(touchPoint.id())); + sceneCurrentTouchPoints[touchPoint.id()] = touchPoint; + } + + StatesAndTouchPoints &statesAndTouchPoints = itemsNeedingEvents[item]; + statesAndTouchPoints.first |= touchPoint.state(); + statesAndTouchPoints.second.append(touchPoint); + } + + if (itemsNeedingEvents.isEmpty()) { + sceneTouchEvent->ignore(); + return; + } + + bool acceptSceneTouchEvent = false; + QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator it = itemsNeedingEvents.constBegin(); + const QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator end = itemsNeedingEvents.constEnd(); + for (; it != end; ++it) { + QGraphicsItem *item = it.key(); + + // determine event type from the state mask + QEvent::Type eventType; + switch (it.value().first) { + case Qt::TouchPointPressed: + // all touch points have pressed state + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + // all touch points have released state + eventType = QEvent::TouchEnd; + break; + case Qt::TouchPointStationary: + // don't send the event if nothing changed + continue; + default: + // all other combinations + eventType = QEvent::TouchUpdate; + break; + } + + QTouchEvent touchEvent(eventType); + touchEvent.setWidget(sceneTouchEvent->widget()); + touchEvent.setDeviceType(sceneTouchEvent->deviceType()); + touchEvent.setModifiers(sceneTouchEvent->modifiers()); + touchEvent.setTouchPointStates(it.value().first); + touchEvent.setTouchPoints(it.value().second); + + switch (touchEvent.type()) { + case QEvent::TouchBegin: + { + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + item->d_ptr->acceptedTouchBeginEvent = true; + bool res = sendTouchBeginEvent(item, &touchEvent) + && touchEvent.isAccepted(); + acceptSceneTouchEvent = acceptSceneTouchEvent || res; + break; + } + default: + if (item->d_ptr->acceptedTouchBeginEvent) { + updateTouchPointsForItem(item, &touchEvent); + (void) sendEvent(item, &touchEvent); + acceptSceneTouchEvent = true; + } + break; + } + } + sceneTouchEvent->setAccepted(acceptSceneTouchEvent); +} + +bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEvent *touchEvent) +{ + Q_Q(QGraphicsScene); + + if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) { + const QTouchEvent::TouchPoint &firstTouchPoint = touchEvent->touchPoints().first(); + cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.screenPos().toPoint(), + firstTouchPoint.scenePos(), + touchEvent->widget()); + } + Q_ASSERT(cachedItemsUnderMouse.first() == origin); + + // Set focus on the topmost enabled item that can take focus. + bool setFocus = false; + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + setFocus = true; + if (item != q->focusItem()) + q->setFocusItem(item, Qt::MouseFocusReason); + break; + } + } + } + + // If nobody could take focus, clear it. + if (!stickyFocus && !setFocus) + q->setFocusItem(0, Qt::MouseFocusReason); + + bool res = false; + bool eventAccepted = touchEvent->isAccepted(); + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + // first, try to deliver the touch event + updateTouchPointsForItem(item, touchEvent); + bool acceptTouchEvents = item->acceptTouchEvents(); + touchEvent->setAccepted(acceptTouchEvents); + res = acceptTouchEvents && sendEvent(item, touchEvent); + eventAccepted = touchEvent->isAccepted(); + item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted); + touchEvent->spont = false; + if (res && eventAccepted) { + // the first item to accept the TouchBegin gets an implicit grab. + for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); + itemForTouchPointId[touchPoint.id()] = item; + } + break; + } + } + + touchEvent->setAccepted(eventAccepted); + return res; +} + +void QGraphicsScenePrivate::enableTouchEventsOnViews() +{ + foreach (QGraphicsView *view, views) + view->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true); +} + +void QGraphicsScenePrivate::updateInputMethodSensitivityInViews() +{ + for (int i = 0; i < views.size(); ++i) + views.at(i)->d_func()->updateInputMethodSensitivity(); +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 0ee4928..c0c6e75 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -83,6 +83,7 @@ class QGraphicsSimpleTextItem; class QGraphicsTextItem; class QGraphicsView; class QGraphicsWidget; +class QGraphicsSceneIndex; class QHelpEvent; class QInputMethodEvent; class QKeyEvent; @@ -152,22 +153,39 @@ public: QRectF itemsBoundingRect() const; QList<QGraphicsItem *> items() const; - QList<QGraphicsItem *> items(const QPointF &pos) const; - QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; - QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; - QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> items(Qt::SortOrder order) const; // ### Qt 5: unify + + QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + + QList<QGraphicsItem *> items(const QPointF &pos) const; // ### obsolete + QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete + QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete + QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete + QList<QGraphicsItem *> collidingItems(const QGraphicsItem *item, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; - QGraphicsItem *itemAt(const QPointF &pos) const; + + QGraphicsItem *itemAt(const QPointF &pos) const; // ### obsolete + QGraphicsItem *itemAt(const QPointF &pos, const QTransform &deviceTransform) const; inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const - { return items(QRectF(x, y, w, h), mode); } - inline QGraphicsItem *itemAt(qreal x, qreal y) const + { return items(QRectF(x, y, w, h), mode); } // ### obsolete + inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode, Qt::SortOrder order, + const QTransform &deviceTransform = QTransform()) const + { return items(QRectF(x, y, w, h), mode, order, deviceTransform); } + inline QGraphicsItem *itemAt(qreal x, qreal y) const // ### obsolete { return itemAt(QPointF(x, y)); } + inline QGraphicsItem *itemAt(qreal x, qreal y, const QTransform &deviceTransform) const + { return itemAt(QPointF(x, y), deviceTransform); } QList<QGraphicsItem *> selectedItems() const; QPainterPath selectionArea() const; - void setSelectionArea(const QPainterPath &path); - void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode); + void setSelectionArea(const QPainterPath &path); // ### obsolete + void setSelectionArea(const QPainterPath &path, const QTransform &deviceTransform); + void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode); // ### obsolete + void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode, const QTransform &deviceTransform); QGraphicsItemGroup *createItemGroup(const QList<QGraphicsItem *> &items); void destroyItemGroup(QGraphicsItemGroup *group); @@ -189,7 +207,7 @@ public: inline QGraphicsRectItem *addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush()) { return addRect(QRectF(x, y, w, h), pen, brush); } void removeItem(QGraphicsItem *item); - + QGraphicsItem *focusItem() const; void setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason = Qt::OtherFocusReason); bool hasFocus() const; @@ -228,6 +246,8 @@ public: QGraphicsWidget *activeWindow() const; void setActiveWindow(QGraphicsWidget *widget); + bool sendEvent(QGraphicsItem *item, QEvent *event); + public Q_SLOTS: void update(const QRectF &rect = QRectF()); void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers); @@ -271,23 +291,21 @@ Q_SIGNALS: void selectionChanged(); private: - void itemUpdated(QGraphicsItem *item, const QRectF &rect); - Q_DECLARE_PRIVATE(QGraphicsScene) Q_DISABLE_COPY(QGraphicsScene) - Q_PRIVATE_SLOT(d_func(), void _q_updateIndex()) Q_PRIVATE_SLOT(d_func(), void _q_emitUpdated()) - Q_PRIVATE_SLOT(d_func(), void _q_removeItemLater(QGraphicsItem *item)) - Q_PRIVATE_SLOT(d_func(), void _q_updateLater()) Q_PRIVATE_SLOT(d_func(), void _q_polishItems()) - Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache()) - Q_PRIVATE_SLOT(d_func(), void _q_resetDirtyItems()) + Q_PRIVATE_SLOT(d_func(), void _q_processDirtyItems()) friend class QGraphicsItem; friend class QGraphicsItemPrivate; friend class QGraphicsView; friend class QGraphicsViewPrivate; friend class QGraphicsWidget; friend class QGraphicsWidgetPrivate; + friend class QGraphicsSceneIndex; + friend class QGraphicsSceneIndexPrivate; + friend class QGraphicsSceneBspTreeIndex; + friend class QGraphicsSceneBspTreeIndexPrivate; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers) diff --git a/src/gui/graphicsview/qgraphicsscene_bsp.cpp b/src/gui/graphicsview/qgraphicsscene_bsp.cpp index 3f3e58b..fb4b9a4 100644 --- a/src/gui/graphicsview/qgraphicsscene_bsp.cpp +++ b/src/gui/graphicsview/qgraphicsscene_bsp.cpp @@ -70,12 +70,15 @@ class QGraphicsSceneFindItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor { public: QList<QGraphicsItem *> *foundItems; + bool onlyTopLevelItems; void visit(QList<QGraphicsItem *> *items) { for (int i = 0; i < items->size(); ++i) { QGraphicsItem *item = items->at(i); - if (!item->d_func()->itemDiscovered && item->isVisible()) { + if (onlyTopLevelItems && item->d_ptr->parent) + item = item->topLevelItem(); + if (!item->d_func()->itemDiscovered && item->d_ptr->visible) { item->d_func()->itemDiscovered = 1; foundItems->prepend(item); } @@ -143,19 +146,15 @@ void QGraphicsSceneBspTree::removeItems(const QSet<QGraphicsItem *> &items) } } -QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect) +QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect, bool onlyTopLevelItems) const { QList<QGraphicsItem *> tmp; findVisitor->foundItems = &tmp; + findVisitor->onlyTopLevelItems = onlyTopLevelItems; climbTree(findVisitor, rect); - return tmp; -} - -QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QPointF &pos) -{ - QList<QGraphicsItem *> tmp; - findVisitor->foundItems = &tmp; - climbTree(findVisitor, pos); + // Reset discovery bits. + for (int i = 0; i < tmp.size(); ++i) + tmp.at(i)->d_ptr->itemDiscovered = 0; return tmp; } @@ -235,47 +234,17 @@ void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth, int index) } } -void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index) -{ - if (nodes.isEmpty()) - return; - - const Node &node = nodes.at(index); - int childIndex = firstChildIndex(index); - - switch (node.type) { - case Node::Leaf: { - visitor->visit(&leaves[node.leafIndex]); - break; - } - case Node::Vertical: - if (pos.x() < node.offset) { - climbTree(visitor, pos, childIndex); - } else { - climbTree(visitor, pos, childIndex + 1); - } - break; - case Node::Horizontal: - if (pos.y() < node.offset) { - climbTree(visitor, pos, childIndex); - } else { - climbTree(visitor, pos, childIndex + 1); - } - break; - } -} - -void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index) +void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index) const { if (nodes.isEmpty()) return; const Node &node = nodes.at(index); - int childIndex = firstChildIndex(index); + const int childIndex = firstChildIndex(index); switch (node.type) { case Node::Leaf: { - visitor->visit(&leaves[node.leafIndex]); + visitor->visit(const_cast<QList<QGraphicsItem*>*>(&leaves[node.leafIndex])); break; } case Node::Vertical: @@ -288,7 +257,6 @@ void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, con } break; case Node::Horizontal: - int childIndex = firstChildIndex(index); if (rect.top() < node.offset) { climbTree(visitor, rect, childIndex); if (rect.bottom() >= node.offset) diff --git a/src/gui/graphicsview/qgraphicsscene_bsp_p.h b/src/gui/graphicsview/qgraphicsscene_bsp_p.h index 73a937f..4cac64a 100644 --- a/src/gui/graphicsview/qgraphicsscene_bsp_p.h +++ b/src/gui/graphicsview/qgraphicsscene_bsp_p.h @@ -92,8 +92,7 @@ public: void removeItem(QGraphicsItem *item, const QRectF &rect); void removeItems(const QSet<QGraphicsItem *> &items); - QList<QGraphicsItem *> items(const QRectF &rect); - QList<QGraphicsItem *> items(const QPointF &pos); + QList<QGraphicsItem *> items(const QRectF &rect, bool onlyTopLevelItems = false) const; int leafCount() const; inline int firstChildIndex(int index) const @@ -106,11 +105,7 @@ public: private: void initialize(const QRectF &rect, int depth, int index); - void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index = 0); - void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0); - - void findItems(QList<QGraphicsItem *> *foundItems, const QRectF &rect, int index); - void findItems(QList<QGraphicsItem *> *foundItems, const QPointF &pos, int index); + void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0) const; QRectF rectForIndex(int index) const; QVector<Node> nodes; diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 727132a..a4bbdd2 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -57,7 +57,8 @@ #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW -#include "qgraphicsscene_bsp_p.h" +#include "qgraphicssceneevent.h" +#include "qgraphicsview.h" #include "qgraphicsitem_p.h" #include <private/qobject_p.h> @@ -68,9 +69,11 @@ #include <QtGui/qfont.h> #include <QtGui/qpalette.h> #include <QtGui/qstyle.h> +#include <QtGui/qstyleoption.h> QT_BEGIN_NAMESPACE +class QGraphicsSceneIndex; class QGraphicsView; class QGraphicsWidget; @@ -81,61 +84,45 @@ public: QGraphicsScenePrivate(); void init(); + static QGraphicsScenePrivate *get(QGraphicsScene *q); + quint32 changedSignalMask; QGraphicsScene::ItemIndexMethod indexMethod; - int bspTreeDepth; - - QList<QGraphicsItem *> estimateItemsInRect(const QRectF &rect) const; - void addToIndex(QGraphicsItem *item); - void removeFromIndex(QGraphicsItem *item); - void resetIndex(); + QGraphicsSceneIndex *index; - QGraphicsSceneBspTree bspTree; - void _q_updateIndex(); int lastItemCount; QRectF sceneRect; bool hasSceneRect; + bool dirtyGrowingItemsBoundingRect; QRectF growingItemsBoundingRect; - QRectF largestUntransformableItem; void _q_emitUpdated(); QList<QRectF> updatedRects; bool updateAll; bool calledEmitUpdated; + bool processDirtyItemsEmitted; QPainterPath selectionArea; int selectionChanging; QSet<QGraphicsItem *> selectedItems; - QList<QGraphicsItem *> unindexedItems; - QList<QGraphicsItem *> indexedItems; - QList<QGraphicsItem *> dirtyItems; - QList<QGraphicsItem *> pendingUpdateItems; QList<QGraphicsItem *> unpolishedItems; + QList<QGraphicsItem *> topLevelItems; + bool needSortTopLevelItems; QMap<QGraphicsItem *, QPointF> movingItemsInitialPositions; + void registerTopLevelItem(QGraphicsItem *item); + void unregisterTopLevelItem(QGraphicsItem *item); void _q_updateLater(); void _q_polishItems(); - void _q_resetDirtyItems(); - void resetDirtyItemsLater(); - bool dirtyItemResetPending; - - QList<int> freeItemIndexes; - bool regenerateIndex; + void _q_processDirtyItems(); - bool purgePending; - void _q_removeItemLater(QGraphicsItem *item); - QSet<QGraphicsItem *> removedItems; - void purgeRemovedItems(); + void removeItemHelper(QGraphicsItem *item); QBrush backgroundBrush; QBrush foregroundBrush; - int indexTimerId; - bool restartIndexTimer; - void startIndexTimer(); - bool stickyFocus; bool hasFocus; QGraphicsItem *focusItem; @@ -143,6 +130,8 @@ public: QGraphicsWidget *tabFocusFirst; QGraphicsWidget *activeWindow; int activationRefCount; + void setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason); + QMultiHash<QGraphicsItem *, QGraphicsItem *> focusProxyReverseMap; QList<QGraphicsWidget *> popupWidgets; void addPopup(QGraphicsWidget *widget); @@ -159,27 +148,33 @@ public: void grabKeyboard(QGraphicsItem *item); void ungrabKeyboard(QGraphicsItem *item, bool itemIsDying = false); void clearKeyboardGrabber(); - + QGraphicsItem *dragDropItem; QGraphicsWidget *enterWidget; Qt::DropAction lastDropAction; QList<QGraphicsItem *> cachedItemsUnderMouse; QList<QGraphicsItem *> hoverItems; + bool allItemsIgnoreHoverEvents; + bool allItemsUseDefaultCursor; + void enableMouseTrackingOnViews(); QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownPos; QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownScenePos; QMap<Qt::MouseButton, QPoint> mouseGrabberButtonDownScreenPos; QList<QGraphicsItem *> itemsAtPosition(const QPoint &screenPos, const QPointF &scenePos, QWidget *widget) const; - static bool itemCollidesWithPath(QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode); void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event); QList<QGraphicsView *> views; + void addView(QGraphicsView *view); + void removeView(QGraphicsView *view); + bool painterStateProtection; QMultiMap<QGraphicsItem *, QGraphicsItem *> sceneEventFilters; void installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter); void removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter); + bool filterDescendantEvent(QGraphicsItem *item, QEvent *event); bool filterEvent(QGraphicsItem *item, QEvent *event); bool sendEvent(QGraphicsItem *item, QEvent *event); @@ -197,56 +192,51 @@ public: void mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent); QGraphicsWidget *windowForItem(const QGraphicsItem *item) const; - QList<QGraphicsItem *> items_helper(const QPointF &pos) const; - QList<QGraphicsItem *> items_helper(const QRectF &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const; - QList<QGraphicsItem *> items_helper(const QPolygonF &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const; - QList<QGraphicsItem *> items_helper(const QPainterPath &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPointF &pos) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QRectF &rect, - Qt::ItemSelectionMode mode) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPolygonF &polygon, - Qt::ItemSelectionMode mode) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPainterPath &path, - Qt::ItemSelectionMode mode) const; - - bool sortCacheEnabled; - bool updatingSortCache; - void invalidateSortCache(); - static void climbTree(QGraphicsItem *item, int *stackingOrder); - void _q_updateSortCache(); - - static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); - static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); - - static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) - { - return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder; - } - static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) - { - return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder; - } - - static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, bool cached); + bool sortCacheEnabled; // for compatibility void drawItemHelper(QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, bool painterStateProtection); - + + void drawItems(QPainter *painter, const QTransform *const viewTransform, + QRegion *exposedRegion, QWidget *widget); + + void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const, + QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0)); + void markDirty(QGraphicsItem *item, const QRectF &rect = QRectF(), bool invalidateChildren = false, + bool maybeDirtyClipPath = false, bool force = false, bool ignoreOpacity = false, + bool removingItemFromScene = false); + void processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren = false, + qreal parentOpacity = qreal(1.0)); + + inline void resetDirtyItem(QGraphicsItem *item, bool recursive = false) + { + Q_ASSERT(item); + item->d_ptr->dirty = 0; + item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0; + item->d_ptr->geometryChanged = 0; + if (!item->d_ptr->dirtyChildren) + recursive = false; + item->d_ptr->dirtyChildren = 0; + item->d_ptr->needsRepaint = QRectF(); + item->d_ptr->allChildrenDirty = 0; + item->d_ptr->fullUpdatePending = 0; + item->d_ptr->ignoreVisible = 0; + item->d_ptr->ignoreOpacity = 0; + if (recursive) { + for (int i = 0; i < item->d_ptr->children.size(); ++i) + resetDirtyItem(item->d_ptr->children.at(i), recursive); + } + } + + inline void ensureSortedTopLevelItems() + { + if (needSortTopLevelItems) { + qSort(topLevelItems.begin(), topLevelItems.end(), qt_notclosestLeaf); + needSortTopLevelItems = false; + } + } + QStyle *style; QFont font; void setFont_helper(const QFont &font); @@ -257,11 +247,39 @@ public: void resolvePalette(); void updatePalette(const QPalette &palette); - mutable QVector<QTransform> sceneTransformCache; - mutable QBitArray validTransforms; - mutable QVector<int> freeSceneTransformSlots; + QStyleOptionGraphicsItem styleOptionTmp; + + QMap<int, QTouchEvent::TouchPoint> sceneCurrentTouchPoints; + QMap<int, QGraphicsItem *> itemForTouchPointId; + static void updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent); + int findClosestTouchPointId(const QPointF &scenePos); + void touchEventHandler(QTouchEvent *touchEvent); + bool sendTouchBeginEvent(QGraphicsItem *item, QTouchEvent *touchEvent); + bool allItemsIgnoreTouchEvents; + void enableTouchEventsOnViews(); + + void updateInputMethodSensitivityInViews(); }; +// QRectF::intersects() returns false always if either the source or target +// rectangle's width or height are 0. This works around that problem. +static inline void _q_adjustRect(QRectF *rect) +{ + Q_ASSERT(rect); + if (!rect->width()) + rect->adjust(-0.00001, 0, 0.00001, 0); + if (!rect->height()) + rect->adjust(0, -0.00001, 0, 0.00001); +} + +static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) +{ + Q_ASSERT(item); + QRectF boundingRect(item->boundingRect()); + _q_adjustRect(&boundingRect); + return boundingRect; +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp new file mode 100644 index 0000000..07d23b7 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -0,0 +1,784 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsSceneBspTreeIndex + \brief The QGraphicsSceneBspTreeIndex class provides an implementation of + a BSP indexing algorithm for discovering items in QGraphicsScene. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + \internal + + QGraphicsSceneBspTreeIndex index use a BSP(Binary Space Partitioning) + implementation to discover items quickly. This implementation is + very efficient for static scene. It has a depth that you can set. + The depth directly affects performance and memory usage; the latter + growing exponentially with the depth of the tree. With an optimal tree + depth, the index can instantly determine the locality of items, even + for scenes with thousands or millions of items. This also greatly improves + rendering performance. + + By default, the value is 0, in which case Qt will guess a reasonable + default depth based on the size, location and number of items in the + scene. If these parameters change frequently, however, you may experience + slowdowns as the index retunes the depth internally. You can avoid + potential slowdowns by fixating the tree depth through setting this + property. + + The depth of the tree and the size of the scene rectangle decide the + granularity of the scene's partitioning. The size of each scene segment is + determined by the following algorithm: + + The BSP tree has an optimal size when each segment contains between 0 and + 10 items. + + \sa QGraphicsScene, QGraphicsView, QGraphicsSceneIndex +*/ + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_GRAPHICSVIEW + +#include <private/qgraphicsscene_p.h> +#include <private/qgraphicsscenebsptreeindex_p.h> +#include <private/qgraphicssceneindex_p.h> + +#include <QtCore/qmath.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +static inline int intmaxlog(int n) +{ + return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0); +} + +/*! + Constructs a private scene bsp index. +*/ +QGraphicsSceneBspTreeIndexPrivate::QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene) + : QGraphicsSceneIndexPrivate(scene), + bspTreeDepth(0), + indexTimerId(0), + restartIndexTimer(false), + regenerateIndex(true), + lastItemCount(0), + purgePending(false), + sortCacheEnabled(false), + updatingSortCache(false) +{ +} + + +/*! + This method will update the BSP index by removing the items from the temporary + unindexed list and add them in the indexedItems list. This will also + update the growingItemsBoundingRect if needed. This will update the BSP + implementation as well. + + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex() +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (!indexTimerId) + return; + + q->killTimer(indexTimerId); + indexTimerId = 0; + + purgeRemovedItems(); + + // Add unindexedItems to indexedItems + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + Q_ASSERT(!item->d_ptr->itemDiscovered); + if (!freeItemIndexes.isEmpty()) { + int freeIndex = freeItemIndexes.takeFirst(); + item->d_func()->index = freeIndex; + indexedItems[freeIndex] = item; + } else { + item->d_func()->index = indexedItems.size(); + indexedItems << item; + } + } + } + + // Determine whether we should regenerate the BSP tree. + if (bspTreeDepth == 0) { + int oldDepth = intmaxlog(lastItemCount); + bspTreeDepth = intmaxlog(indexedItems.size()); + static const int slack = 100; + if (bsp.leafCount() == 0 || (oldDepth != bspTreeDepth && qAbs(lastItemCount - indexedItems.size()) > slack)) { + // ### Crude algorithm. + regenerateIndex = true; + } + } + + // Regenerate the tree. + if (regenerateIndex) { + regenerateIndex = false; + bsp.initialize(sceneRect, bspTreeDepth); + unindexedItems = indexedItems; + lastItemCount = indexedItems.size(); + } + + // Insert all unindexed items into the tree. + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + if (item->d_ptr->itemIsUntransformable()) { + untransformableItems << item; + continue; + } + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + continue; + + bsp.insertItem(item, item->sceneBoundingRect()); + } + } + unindexedItems.clear(); +} + + +/*! + \internal + + Removes stale pointers from all data structures. +*/ +void QGraphicsSceneBspTreeIndexPrivate::purgeRemovedItems() +{ + if (!purgePending && removedItems.isEmpty()) + return; + + // Remove stale items from the BSP tree. + bsp.removeItems(removedItems); + // Purge this list. + removedItems.clear(); + freeItemIndexes.clear(); + for (int i = 0; i < indexedItems.size(); ++i) { + if (!indexedItems.at(i)) + freeItemIndexes << i; + } + purgePending = false; +} + +/*! + \internal + + Starts or restarts the timer used for reindexing unindexed items. +*/ +void QGraphicsSceneBspTreeIndexPrivate::startIndexTimer(int interval) +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (indexTimerId) { + restartIndexTimer = true; + } else { + indexTimerId = q->startTimer(interval); + } +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::resetIndex() +{ + purgeRemovedItems(); + for (int i = 0; i < indexedItems.size(); ++i) { + if (QGraphicsItem *item = indexedItems.at(i)) { + item->d_ptr->index = -1; + Q_ASSERT(!item->d_ptr->itemDiscovered); + unindexedItems << item; + } + } + indexedItems.clear(); + freeItemIndexes.clear(); + untransformableItems.clear(); + regenerateIndex = true; + startIndexTimer(); +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::climbTree(QGraphicsItem *item, int *stackingOrder) +{ + if (!item->d_ptr->children.isEmpty()) { + QList<QGraphicsItem *> childList = item->d_ptr->children; + qSort(childList.begin(), childList.end(), qt_closestLeaf); + for (int i = 0; i < childList.size(); ++i) { + QGraphicsItem *item = childList.at(i); + if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent)) + climbTree(childList.at(i), stackingOrder); + } + item->d_ptr->globalStackingOrder = (*stackingOrder)++; + for (int i = 0; i < childList.size(); ++i) { + QGraphicsItem *item = childList.at(i); + if (item->flags() & QGraphicsItem::ItemStacksBehindParent) + climbTree(childList.at(i), stackingOrder); + } + } else { + item->d_ptr->globalStackingOrder = (*stackingOrder)++; + } +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::_q_updateSortCache() +{ + Q_Q(QGraphicsSceneBspTreeIndex); + _q_updateIndex(); + + if (!sortCacheEnabled || !updatingSortCache) + return; + + updatingSortCache = false; + int stackingOrder = 0; + + QList<QGraphicsItem *> topLevels; + const QList<QGraphicsItem *> items = q->items(); + for (int i = 0; i < items.size(); ++i) { + QGraphicsItem *item = items.at(i); + if (item && !item->d_ptr->parent) + topLevels << item; + } + + qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf); + for (int i = 0; i < topLevels.size(); ++i) + climbTree(topLevels.at(i), &stackingOrder); +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::invalidateSortCache() +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (!sortCacheEnabled || updatingSortCache) + return; + + updatingSortCache = true; + QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection); +} + +void QGraphicsSceneBspTreeIndexPrivate::addItem(QGraphicsItem *item, bool recursive) +{ + if (!item) + return; + + // Prevent reusing a recently deleted pointer: purge all removed item from our lists. + purgeRemovedItems(); + + // Invalidate any sort caching; arrival of a new item means we need to resort. + // Update the scene's sort cache settings. + item->d_ptr->globalStackingOrder = -1; + invalidateSortCache(); + + // Indexing requires sceneBoundingRect(), but because \a item might + // not be completely constructed at this point, we need to store it in + // a temporary list and schedule an indexing for later. + if (item->d_ptr->index == -1) { + Q_ASSERT(!unindexedItems.contains(item)); + unindexedItems << item; + startIndexTimer(0); + } else { + Q_ASSERT(indexedItems.contains(item)); + qWarning("QGraphicsSceneBspTreeIndex::addItem: item has already been added to this BSP"); + } + + if (recursive) { + for (int i = 0; i < item->d_ptr->children.size(); ++i) + addItem(item->d_ptr->children.at(i), recursive); + } +} + +void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool recursive, + bool moveToUnindexedItems) +{ + if (!item) + return; + + if (item->d_ptr->index != -1) { + Q_ASSERT(item->d_ptr->index < indexedItems.size()); + Q_ASSERT(indexedItems.at(item->d_ptr->index) == item); + Q_ASSERT(!item->d_ptr->itemDiscovered); + freeItemIndexes << item->d_ptr->index; + indexedItems[item->d_ptr->index] = 0; + item->d_ptr->index = -1; + + if (item->d_ptr->itemIsUntransformable()) { + untransformableItems.removeOne(item); + } else if (item->d_ptr->inDestructor) { + // Avoid virtual function calls from the destructor. + purgePending = true; + removedItems << item; + } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + bsp.removeItem(item, item->sceneBoundingRect()); + } + } else { + unindexedItems.removeOne(item); + } + invalidateSortCache(); // ### Only do this when removing from BSP? + + Q_ASSERT(item->d_ptr->index == -1); + Q_ASSERT(!indexedItems.contains(item)); + Q_ASSERT(!unindexedItems.contains(item)); + Q_ASSERT(!untransformableItems.contains(item)); + + if (moveToUnindexedItems) + addItem(item); + + if (recursive) { + for (int i = 0; i < item->d_ptr->children.size(); ++i) + removeItem(item->d_ptr->children.at(i), recursive, moveToUnindexedItems); + } +} + +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndexPrivate::estimateItems(const QRectF &rect, Qt::SortOrder order, + bool onlyTopLevelItems) +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (onlyTopLevelItems && rect.isNull()) + return q->QGraphicsSceneIndex::estimateTopLevelItems(rect, order); + + purgeRemovedItems(); + _q_updateSortCache(); + Q_ASSERT(unindexedItems.isEmpty()); + + QList<QGraphicsItem *> rectItems = bsp.items(rect, onlyTopLevelItems); + if (onlyTopLevelItems) { + for (int i = 0; i < untransformableItems.size(); ++i) { + QGraphicsItem *item = untransformableItems.at(i); + if (!item->d_ptr->parent) { + rectItems << item; + } else { + item = item->topLevelItem(); + if (!rectItems.contains(item)) + rectItems << item; + } + } + } else { + rectItems += untransformableItems; + } + + sortItems(&rectItems, order, sortCacheEnabled, onlyTopLevelItems); + return rectItems; +} + +/*! + Returns true if \a item1 is on top of \a item2. + + \internal +*/ +bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Siblings? Just check their z-values. + const QGraphicsItemPrivate *d1 = item1->d_ptr; + const QGraphicsItemPrivate *d2 = item2->d_ptr; + if (d1->parent == d2->parent) + return qt_closestLeaf(item1, item2); + + // Find common ancestor, and each item's ancestor closest to the common + // ancestor. + int item1Depth = d1->depth; + int item2Depth = d2->depth; + const QGraphicsItem *p = item1; + const QGraphicsItem *t1 = item1; + while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { + if (p == item2) { + // item2 is one of item1's ancestors; item1 is on top + return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t1 = p; + --item1Depth; + } + p = item2; + const QGraphicsItem *t2 = item2; + while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { + if (p == item1) { + // item1 is one of item2's ancestors; item1 is not on top + return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t2 = p; + --item2Depth; + } + + // item1Ancestor is now at the same level as item2Ancestor, but not the same. + const QGraphicsItem *a1 = t1; + const QGraphicsItem *a2 = t2; + while (a1) { + const QGraphicsItem *p1 = a1; + const QGraphicsItem *p2 = a2; + a1 = a1->parentItem(); + a2 = a2->parentItem(); + if (a1 && a1 == a2) + return qt_closestLeaf(p1, p2); + } + + // No common ancestor? Then just compare the items' toplevels directly. + return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem()); +} + +/*! + Returns true if \a item2 is on top of \a item1. + + \internal +*/ +bool QGraphicsSceneBspTreeIndexPrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + return closestItemFirst_withoutCache(item2, item1); +} + +/*! + Sort a list of \a itemList in a specific \a order and use the cache if requested. + + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, + bool sortCacheEnabled, bool onlyTopLevelItems) +{ + if (order == Qt::SortOrder(-1)) + return; + + if (onlyTopLevelItems) { + if (order == Qt::AscendingOrder) + qSort(itemList->begin(), itemList->end(), qt_closestLeaf); + else if (order == Qt::DescendingOrder) + qSort(itemList->begin(), itemList->end(), qt_notclosestLeaf); + return; + } + + if (sortCacheEnabled) { + if (order == Qt::AscendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache); + } else if (order == Qt::DescendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemLast_withCache); + } + } else { + if (order == Qt::AscendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); + } else if (order == Qt::DescendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); + } + } +} + +/*! + Constructs a BSP scene index for the given \a scene. +*/ +QGraphicsSceneBspTreeIndex::QGraphicsSceneBspTreeIndex(QGraphicsScene *scene) + : QGraphicsSceneIndex(*new QGraphicsSceneBspTreeIndexPrivate(scene), scene) +{ + +} + +QGraphicsSceneBspTreeIndex::~QGraphicsSceneBspTreeIndex() +{ + Q_D(QGraphicsSceneBspTreeIndex); + for (int i = 0; i < d->indexedItems.size(); ++i) { + // Ensure item bits are reset properly. + if (QGraphicsItem *item = d->indexedItems.at(i)) { + Q_ASSERT(!item->d_ptr->itemDiscovered); + item->d_ptr->index = -1; + } + } +} + +/*! + \internal + Clear the all the BSP index. +*/ +void QGraphicsSceneBspTreeIndex::clear() +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->bsp.clear(); + d->lastItemCount = 0; + d->freeItemIndexes.clear(); + for (int i = 0; i < d->indexedItems.size(); ++i) { + // Ensure item bits are reset properly. + if (QGraphicsItem *item = d->indexedItems.at(i)) { + Q_ASSERT(!item->d_ptr->itemDiscovered); + item->d_ptr->index = -1; + } + } + d->indexedItems.clear(); + d->unindexedItems.clear(); + d->untransformableItems.clear(); + d->regenerateIndex = true; +} + +/*! + Add the \a item into the BSP index. +*/ +void QGraphicsSceneBspTreeIndex::addItem(QGraphicsItem *item) +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->addItem(item); +} + +/*! + Remove the \a item from the BSP index. +*/ +void QGraphicsSceneBspTreeIndex::removeItem(QGraphicsItem *item) +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->removeItem(item); +} + +/*! + \internal + Update the BSP when the \a item 's bounding rect has changed. +*/ +void QGraphicsSceneBspTreeIndex::prepareBoundingRectChange(const QGraphicsItem *item) +{ + if (!item) + return; + + if (item->d_ptr->index == -1 || item->d_ptr->itemIsUntransformable() + || (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + return; // Item is not in BSP tree; nothing to do. + } + + Q_D(QGraphicsSceneBspTreeIndex); + QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); + d->removeItem(thatItem, /*recursive=*/false, /*moveToUnindexedItems=*/true); + for (int i = 0; i < item->d_ptr->children.size(); ++i) // ### Do we really need this? + prepareBoundingRectChange(item->d_ptr->children.at(i)); +} + +/*! + Returns an estimation visible items that are either inside or + intersect with the specified \a rect and return a list sorted using \a order. + + \a deviceTransform is the transformation apply to the view. + +*/ +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateItems(const QRectF &rect, Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneBspTreeIndex); + return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order); +} + +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneBspTreeIndex); + return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order, /*onlyTopLevels=*/true); +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const; + + Return all items in the BSP index and sort them using \a order. +*/ +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneBspTreeIndex); + const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->purgeRemovedItems(); + QList<QGraphicsItem *> itemList; + + // If freeItemIndexes is empty, we know there are no holes in indexedItems and + // unindexedItems. + if (d->freeItemIndexes.isEmpty()) { + if (d->unindexedItems.isEmpty()) { + itemList = d->indexedItems; + } else { + itemList = d->indexedItems + d->unindexedItems; + } + } else { + // Rebuild the list of items to avoid holes. ### We could also just + // compress the item lists at this point. + foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) { + if (item) + itemList << item; + } + } + if (order != -1) { + //We sort descending order + d->sortItems(&itemList, order, d->sortCacheEnabled); + } + return itemList; +} + +/*! + \property QGraphicsSceneBspTreeIndex::bspTreeDepth + \brief the depth of the BSP index tree + \since 4.6 + + This value determines the depth of BSP tree. The depth + directly affects performance and memory usage; the latter + growing exponentially with the depth of the tree. With an optimal tree + depth, the index can instantly determine the locality of items, even + for scenes with thousands or millions of items. This also greatly improves + rendering performance. + + By default, the value is 0, in which case Qt will guess a reasonable + default depth based on the size, location and number of items in the + scene. If these parameters change frequently, however, you may experience + slowdowns as the index retunes the depth internally. You can avoid + potential slowdowns by fixating the tree depth through setting this + property. + + The depth of the tree and the size of the scene rectangle decide the + granularity of the scene's partitioning. The size of each scene segment is + determined by the following algorithm: + + The BSP tree has an optimal size when each segment contains between 0 and + 10 items. + +*/ +int QGraphicsSceneBspTreeIndex::bspTreeDepth() +{ + Q_D(const QGraphicsSceneBspTreeIndex); + return d->bspTreeDepth; +} + +void QGraphicsSceneBspTreeIndex::setBspTreeDepth(int depth) +{ + Q_D(QGraphicsSceneBspTreeIndex); + if (d->bspTreeDepth == depth) + return; + d->bspTreeDepth = depth; + d->resetIndex(); +} + +/*! + \internal + + This method react to the \a rect change of the scene and + reset the BSP tree index. +*/ +void QGraphicsSceneBspTreeIndex::updateSceneRect(const QRectF &rect) +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->sceneRect = rect; + d->resetIndex(); +} + +/*! + \internal + + This method react to the \a change of the \a item and use the \a value to + update the BSP tree if necessary. +*/ +void QGraphicsSceneBspTreeIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value) +{ + Q_D(QGraphicsSceneBspTreeIndex); + switch (change) { + case QGraphicsItem::ItemFlagsChange: { + // Handle ItemIgnoresTransformations + bool ignoredTransform = item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations; + bool willIgnoreTransform = value.toUInt() & QGraphicsItem::ItemIgnoresTransformations; + bool clipsChildren = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape; + bool willClipChildren = value.toUInt() & QGraphicsItem::ItemClipsChildrenToShape; + if ((ignoredTransform != willIgnoreTransform) || (clipsChildren != willClipChildren)) { + QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); + // Remove item and its descendants from the index and append + // them to the list of unindexed items. Then, when the index + // is updated, they will be put into the bsp-tree or the list + // of untransformable items. + d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true); + } + break; + } + case QGraphicsItem::ItemZValueChange: + d->invalidateSortCache(); + break; + case QGraphicsItem::ItemParentChange: { + d->invalidateSortCache(); + // Handle ItemIgnoresTransformations + QGraphicsItem *newParent = qVariantValue<QGraphicsItem *>(value); + bool ignoredTransform = item->d_ptr->itemIsUntransformable(); + bool willIgnoreTransform = (item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations) + || (newParent && newParent->d_ptr->itemIsUntransformable()); + bool ancestorClippedChildren = item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren; + bool ancestorWillClipChildren = newParent + && ((newParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) + || (newParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)); + if ((ignoredTransform != willIgnoreTransform) || (ancestorClippedChildren != ancestorWillClipChildren)) { + QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); + // Remove item and its descendants from the index and append + // them to the list of unindexed items. Then, when the index + // is updated, they will be put into the bsp-tree or the list + // of untransformable items. + d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true); + } + break; + } + default: + break; + } + return QGraphicsSceneIndex::itemChange(item, change, value); +} +/*! + \reimp + + Used to catch the timer event. + + \internal +*/ +bool QGraphicsSceneBspTreeIndex::event(QEvent *event) +{ + Q_D(QGraphicsSceneBspTreeIndex); + switch (event->type()) { + case QEvent::Timer: + if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { + if (d->restartIndexTimer) { + d->restartIndexTimer = false; + } else { + // this call will kill the timer + d->_q_updateIndex(); + } + } + // Fallthrough intended - support timers in subclasses. + default: + return QObject::event(event); + } + return true; +} + +QT_END_NAMESPACE + +#include "moc_qgraphicsscenebsptreeindex_p.cpp" + +#endif // QT_NO_GRAPHICSVIEW + diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h new file mode 100644 index 0000000..2293a8e --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QGRAPHICSBSPTREEINDEX_H +#define QGRAPHICSBSPTREEINDEX_H + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include "qgraphicssceneindex_p.h" +#include "qgraphicsitem_p.h" +#include "qgraphicsscene_bsp_p.h" + +#include <QtCore/qrect.h> +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; + +class QGraphicsScene; +class QGraphicsSceneBspTreeIndexPrivate; + +class Q_AUTOTEST_EXPORT QGraphicsSceneBspTreeIndex : public QGraphicsSceneIndex +{ + Q_OBJECT + Q_PROPERTY(int bspTreeDepth READ bspTreeDepth WRITE setBspTreeDepth) +public: + QGraphicsSceneBspTreeIndex(QGraphicsScene *scene = 0); + ~QGraphicsSceneBspTreeIndex(); + + QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const; + QList<QGraphicsItem *> estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const; + QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const; + + int bspTreeDepth(); + void setBspTreeDepth(int depth); + +protected Q_SLOTS: + void updateSceneRect(const QRectF &rect); + +protected: + bool event(QEvent *event); + void clear(); + + void addItem(QGraphicsItem *item); + void removeItem(QGraphicsItem *item); + void prepareBoundingRectChange(const QGraphicsItem *item); + + void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value); + +private : + Q_DECLARE_PRIVATE(QGraphicsSceneBspTreeIndex) + Q_DISABLE_COPY(QGraphicsSceneBspTreeIndex) + Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache()) + Q_PRIVATE_SLOT(d_func(), void _q_updateIndex()) + + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; +}; + +class QGraphicsSceneBspTreeIndexPrivate : public QGraphicsSceneIndexPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneBspTreeIndex) +public: + QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene); + + QGraphicsSceneBspTree bsp; + QRectF sceneRect; + int bspTreeDepth; + int indexTimerId; + bool restartIndexTimer; + bool regenerateIndex; + int lastItemCount; + + QList<QGraphicsItem *> indexedItems; + QList<QGraphicsItem *> unindexedItems; + QList<QGraphicsItem *> untransformableItems; + QList<int> freeItemIndexes; + + bool purgePending; + QSet<QGraphicsItem *> removedItems; + void purgeRemovedItems(); + + void _q_updateIndex(); + void startIndexTimer(int interval = QGRAPHICSSCENE_INDEXTIMER_TIMEOUT); + void resetIndex(); + + void _q_updateSortCache(); + bool sortCacheEnabled; + bool updatingSortCache; + void invalidateSortCache(); + void addItem(QGraphicsItem *item, bool recursive = false); + void removeItem(QGraphicsItem *item, bool recursive = false, bool moveToUnindexedItems = false); + QList<QGraphicsItem *> estimateItems(const QRectF &, Qt::SortOrder, bool b = false); + + static void climbTree(QGraphicsItem *item, int *stackingOrder); + static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); + static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); + + static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) + { + return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder; + } + static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) + { + return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder; + } + + static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, + bool cached, bool onlyTopLevelItems = false); +}; + +static inline bool QRectF_intersects(const QRectF &s, const QRectF &r) +{ + qreal xp = s.left(); + qreal yp = s.top(); + qreal w = s.width(); + qreal h = s.height(); + qreal l1 = xp; + qreal r1 = xp; + if (w < 0) + l1 += w; + else + r1 += w; + + qreal l2 = r.left(); + qreal r2 = r.left(); + if (w < 0) + l2 += r.width(); + else + r2 += r.width(); + + if (l1 >= r2 || l2 >= r1) + return false; + + qreal t1 = yp; + qreal b1 = yp; + if (h < 0) + t1 += h; + else + b1 += h; + + qreal t2 = r.top(); + qreal b2 = r.top(); + if (r.height() < 0) + t2 += r.height(); + else + b2 += r.height(); + + return !(t1 >= b2 || t2 >= b1); +} + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW + +#endif // QGRAPHICSBSPTREEINDEX_H diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 283010d..92af0cc 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -76,14 +76,14 @@ received by the view (see \l{QGraphicsSceneMouseEvent::}{lastScreenPos()}, \l{QGraphicsSceneMouseEvent::}{lastScenePos()}, and - \l{QGraphicsSceneMouseEvent::}{lastPos()}). + \l{QGraphicsSceneMouseEvent::}{lastPos()}). \sa QEvent */ /*! \class QGraphicsSceneMouseEvent - \brief The QGraphicsSceneMouseEvent class provides mouse events + \brief The QGraphicsSceneMouseEvent class provides mouse events in the graphics view framework. \since 4.2 \ingroup multimedia @@ -106,7 +106,7 @@ /*! \class QGraphicsSceneWheelEvent - \brief The QGraphicsSceneWheelEvent class provides wheel events + \brief The QGraphicsSceneWheelEvent class provides wheel events in the graphics view framework. \brief The QGraphicsSceneWheelEvent class provides wheel events in the graphics view framework. @@ -157,7 +157,7 @@ /*! \class QGraphicsSceneHoverEvent - \brief The QGraphicsSceneHoverEvent class provides hover events + \brief The QGraphicsSceneHoverEvent class provides hover events in the graphics view framework. \since 4.2 \ingroup multimedia @@ -173,7 +173,7 @@ /*! \class QGraphicsSceneHelpEvent - \brief The QGraphicsSceneHelpEvent class provides events when a + \brief The QGraphicsSceneHelpEvent class provides events when a tooltip is requested. \since 4.2 \ingroup multimedia @@ -199,7 +199,7 @@ /*! \class QGraphicsSceneDragDropEvent \brief The QGraphicsSceneDragDropEvent class provides events for - drag and drop in the graphics view framework. + drag and drop in the graphics view framework. \since 4.2 \ingroup multimedia \ingroup graphicsview-api @@ -268,6 +268,10 @@ #include <QtCore/qpoint.h> #include <QtCore/qsize.h> #include <QtCore/qstring.h> +#include "qgraphicsview.h" +#include "qgraphicsitem.h" +#include <QtGui/qgesture.h> +#include <private/qevent_p.h> QT_BEGIN_NAMESPACE @@ -522,7 +526,7 @@ void QGraphicsSceneMouseEvent::setLastPos(const QPointF &pos) } /*! - Returns the last recorded mouse cursor position in scene + Returns the last recorded mouse cursor position in scene coordinates. The last recorded position is the position of the previous mouse event received by the view that created the event. @@ -545,7 +549,7 @@ void QGraphicsSceneMouseEvent::setLastScenePos(const QPointF &pos) } /*! - Returns the last recorded mouse cursor position in screen + Returns the last recorded mouse cursor position in screen coordinates. The last recorded position is the position of the previous mouse event received by the view that created the event. @@ -1275,7 +1279,7 @@ QGraphicsSceneDragDropEvent::~QGraphicsSceneDragDropEvent() /*! Returns the mouse position of the event relative to the view that sent the event. - + \sa QGraphicsView, screenPos(), scenePos() */ QPointF QGraphicsSceneDragDropEvent::pos() const @@ -1373,7 +1377,7 @@ void QGraphicsSceneDragDropEvent::setButtons(Qt::MouseButtons buttons) /*! Returns the keyboard modifiers that were pressed when the drag - and drop event was created. + and drop event was created. \sa Qt::KeyboardModifiers */ @@ -1428,7 +1432,7 @@ void QGraphicsSceneDragDropEvent::setPossibleActions(Qt::DropActions actions) The action must be one of the possible actions as defined by \c possibleActions(). - \sa Qt::DropAction, possibleActions() + \sa Qt::DropAction, possibleActions() */ Qt::DropAction QGraphicsSceneDragDropEvent::proposedAction() const @@ -1472,7 +1476,7 @@ void QGraphicsSceneDragDropEvent::acceptProposedAction() /*! Returns the action that was performed in this drag and drop. This should be set by the receiver of the drop and is - returned by QDrag::start(). + returned by QDrag::exec(). \sa setDropAction(), acceptProposedAction() */ diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index d493de3..b38e757 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -44,6 +44,10 @@ #include <QtCore/qcoreevent.h> #include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtGui/qpolygon.h> +#include <QtCore/qset.h> +#include <QtCore/qhash.h> QT_BEGIN_HEADER diff --git a/src/gui/graphicsview/qgraphicssceneindex.cpp b/src/gui/graphicsview/qgraphicssceneindex.cpp new file mode 100644 index 0000000..ab5ca85 --- /dev/null +++ b/src/gui/graphicsview/qgraphicssceneindex.cpp @@ -0,0 +1,648 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \class QGraphicsSceneIndex + \brief The QGraphicsSceneIndex class provides a base class to implement + a custom indexing algorithm for discovering items in QGraphicsScene. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + \internal + + The QGraphicsSceneIndex class provides a base class to implement + a custom indexing algorithm for discovering items in QGraphicsScene. You + need to subclass it and reimplement addItem, removeItem, estimateItems + and items in order to have an functional indexing. + + \sa QGraphicsScene, QGraphicsView +*/ + +#include "qdebug.h" +#include "qgraphicsscene.h" +#include "qgraphicsitem_p.h" +#include "qgraphicsscene_p.h" +#include "qgraphicswidget.h" +#include "qgraphicssceneindex_p.h" +#include "qgraphicsscenebsptreeindex_p.h" + +#ifndef QT_NO_GRAPHICSVIEW + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneIndexRectIntersector : public QGraphicsSceneIndexIntersector +{ +public: + bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const + { + QRectF brect = item->boundingRect(); + _q_adjustRect(&brect); + + // ### Add test for this (without making things slower?) + Q_UNUSED(exposeRect); + + bool keep = true; + const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item); + if (itemd->itemIsUntransformable()) { + // Untransformable items; map the scene rect to item coordinates. + const QTransform transform = item->deviceTransform(deviceTransform); + QRectF itemRect = (deviceTransform * transform.inverted()).mapRect(sceneRect); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = itemRect.contains(brect) && itemRect != brect; + else + keep = itemRect.intersects(brect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath itemPath; + itemPath.addRect(itemRect); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode); + } + } else { + Q_ASSERT(!itemd->dirtySceneTransform); + const QRectF itemSceneBoundingRect = itemd->sceneTransformTranslateOnly + ? brect.translated(itemd->sceneTransform.dx(), + itemd->sceneTransform.dy()) + : itemd->sceneTransform.mapRect(brect); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = sceneRect != brect && sceneRect.contains(itemSceneBoundingRect); + else + keep = sceneRect.intersects(itemSceneBoundingRect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath rectPath; + rectPath.addRect(sceneRect); + if (itemd->sceneTransformTranslateOnly) + rectPath.translate(-itemd->sceneTransform.dx(), -itemd->sceneTransform.dy()); + else + rectPath = itemd->sceneTransform.inverted().map(rectPath); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, rectPath, mode); + } + } + return keep; + } + + QRectF sceneRect; +}; + +class QGraphicsSceneIndexPointIntersector : public QGraphicsSceneIndexIntersector +{ +public: + bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const + { + QRectF brect = item->boundingRect(); + _q_adjustRect(&brect); + + // ### Add test for this (without making things slower?) + Q_UNUSED(exposeRect); + + bool keep = false; + const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item); + if (itemd->itemIsUntransformable()) { + // Untransformable items; map the scene point to item coordinates. + const QTransform transform = item->deviceTransform(deviceTransform); + QPointF itemPoint = (deviceTransform * transform.inverted()).map(scenePoint); + keep = brect.contains(itemPoint); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath pointPath; + pointPath.addRect(QRectF(itemPoint, QSizeF(1, 1))); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, pointPath, mode); + } + } else { + Q_ASSERT(!itemd->dirtySceneTransform); + QRectF sceneBoundingRect = itemd->sceneTransformTranslateOnly + ? brect.translated(itemd->sceneTransform.dx(), + itemd->sceneTransform.dy()) + : itemd->sceneTransform.mapRect(brect); + keep = sceneBoundingRect.intersects(QRectF(scenePoint, QSizeF(1, 1))); + if (keep) { + QPointF p = itemd->sceneTransformTranslateOnly + ? QPointF(scenePoint.x() - itemd->sceneTransform.dx(), + scenePoint.y() - itemd->sceneTransform.dy()) + : itemd->sceneTransform.inverted().map(scenePoint); + keep = item->contains(p); + } + } + + return keep; + } + + QPointF scenePoint; +}; + +class QGraphicsSceneIndexPathIntersector : public QGraphicsSceneIndexIntersector +{ +public: + bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const + { + QRectF brect = item->boundingRect(); + _q_adjustRect(&brect); + + // ### Add test for this (without making things slower?) + Q_UNUSED(exposeRect); + + bool keep = true; + const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item); + if (itemd->itemIsUntransformable()) { + // Untransformable items; map the scene rect to item coordinates. + const QTransform transform = item->deviceTransform(deviceTransform); + QPainterPath itemPath = (deviceTransform * transform.inverted()).map(scenePath); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = itemPath.contains(brect); + else + keep = itemPath.intersects(brect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode); + } else { + Q_ASSERT(!itemd->dirtySceneTransform); + const QRectF itemSceneBoundingRect = itemd->sceneTransformTranslateOnly + ? brect.translated(itemd->sceneTransform.dx(), + itemd->sceneTransform.dy()) + : itemd->sceneTransform.mapRect(brect); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = scenePath.contains(itemSceneBoundingRect); + else + keep = scenePath.intersects(itemSceneBoundingRect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath itemPath = itemd->sceneTransformTranslateOnly + ? scenePath.translated(-itemd->sceneTransform.dx(), + -itemd->sceneTransform.dy()) + : itemd->sceneTransform.inverted().map(scenePath); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode); + } + } + return keep; + } + + QPainterPath scenePath; +}; + +/*! + Constructs a private scene index. +*/ +QGraphicsSceneIndexPrivate::QGraphicsSceneIndexPrivate(QGraphicsScene *scene) : scene(scene) +{ + pointIntersector = new QGraphicsSceneIndexPointIntersector; + rectIntersector = new QGraphicsSceneIndexRectIntersector; + pathIntersector = new QGraphicsSceneIndexPathIntersector; +} + +/*! + Destructor of private scene index. +*/ +QGraphicsSceneIndexPrivate::~QGraphicsSceneIndexPrivate() +{ + delete pointIntersector; + delete rectIntersector; + delete pathIntersector; +} + +/*! + \internal + + Checks if item collides with the path and mode, but also checks that if it + doesn't collide, maybe its frame rect will. +*/ +bool QGraphicsSceneIndexPrivate::itemCollidesWithPath(const QGraphicsItem *item, + const QPainterPath &path, + Qt::ItemSelectionMode mode) +{ + if (item->collidesWithPath(path, mode)) + return true; + if (item->isWidget()) { + // Check if this is a window, and if its frame rect collides. + const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(item); + if (widget->isWindow()) { + QRectF frameRect = widget->windowFrameRect(); + QPainterPath framePath; + framePath.addRect(frameRect); + bool intersects = path.intersects(frameRect); + if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect) + return intersects || path.contains(frameRect.topLeft()) + || framePath.contains(path.elementAt(0)); + return !intersects && path.contains(frameRect.topLeft()); + } + } + return false; +} + +/*! + \internal +*/ +void QGraphicsSceneIndexPrivate::recursive_items_helper(QGraphicsItem *item, QRectF exposeRect, + QGraphicsSceneIndexIntersector *intersector, + QList<QGraphicsItem *> *items, + const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order, + qreal parentOpacity) const +{ + Q_ASSERT(item); + if (!item->d_ptr->visible) + return; + + const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity); + const bool itemIsFullyTransparent = (opacity < 0.0001); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) + return; + + // Update the item's scene transform if dirty. + const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable(); + const bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform && !itemIsUntransformable; + if (wasDirtyParentSceneTransform) { + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); + } + + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + bool processItem = !itemIsFullyTransparent; + if (processItem) { + processItem = intersector->intersect(item, exposeRect, mode, viewTransform); + if (!processItem && (!itemHasChildren || itemClipsChildrenToShape)) { + if (wasDirtyParentSceneTransform) + item->d_ptr->invalidateChildrenSceneTransform(); + return; + } + } // else we know for sure this item has children we must process. + + int i = 0; + if (itemHasChildren) { + // Sort children. + item->d_ptr->ensureSortedChildren(); + + // Clip to shape. + if (itemClipsChildrenToShape && !itemIsUntransformable) { + QPainterPath mappedShape = item->d_ptr->sceneTransformTranslateOnly + ? item->shape().translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy()) + : item->d_ptr->sceneTransform.map(item->shape()); + exposeRect &= mappedShape.controlPointRect(); + } + + // Process children behind + for (i = 0; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent)) + break; + if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) + continue; + recursive_items_helper(child, exposeRect, intersector, items, viewTransform, + mode, order, opacity); + } + } + + // Process item + if (processItem) + items->append(item); + + // Process children in front + if (itemHasChildren) { + for (; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) + continue; + recursive_items_helper(child, exposeRect, intersector, items, viewTransform, + mode, order, opacity); + } + } +} + +void QGraphicsSceneIndexPrivate::init() +{ + if (!scene) + return; + + QObject::connect(scene, SIGNAL(sceneRectChanged(const QRectF&)), + q_func(), SLOT(updateSceneRect(const QRectF&))); +} + +/*! + Constructs an abstract scene index for a given \a scene. +*/ +QGraphicsSceneIndex::QGraphicsSceneIndex(QGraphicsScene *scene) +: QObject(*new QGraphicsSceneIndexPrivate(scene), scene) +{ + d_func()->init(); +} + +/*! + \internal +*/ +QGraphicsSceneIndex::QGraphicsSceneIndex(QGraphicsSceneIndexPrivate &dd, QGraphicsScene *scene) + : QObject(dd, scene) +{ + d_func()->init(); +} + +/*! + Destroys the scene index. +*/ +QGraphicsSceneIndex::~QGraphicsSceneIndex() +{ + +} + +/*! + Returns the scene of this index. +*/ +QGraphicsScene* QGraphicsSceneIndex::scene() const +{ + Q_D(const QGraphicsSceneIndex); + return d->scene; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPointF &pos, + Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform + &deviceTransform) const + + Returns all visible items that, depending on \a mode, are at the specified + \a pos and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with \a pos are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine the + list to get an exact result. If you want to implement your own refinement + algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPointF &pos, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + + Q_D(const QGraphicsSceneIndex); + QList<QGraphicsItem *> itemList; + d->pointIntersector->scenePoint = pos; + d->items_helper(QRectF(pos, QSizeF(1, 1)), d->pointIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QRectF &rect, + Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform + &deviceTransform) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a rect and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a rect are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine + the list to get an exact result. If you want to implement your own + refinement algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QRectF &rect, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsSceneIndex); + QRectF exposeRect = rect; + _q_adjustRect(&exposeRect); + QList<QGraphicsItem *> itemList; + d->rectIntersector->sceneRect = rect; + d->items_helper(exposeRect, d->rectIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPolygonF + &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const + QTransform &deviceTransform) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a polygon and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a polygon are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine + the list to get an exact result. If you want to implement your own + refinement algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsSceneIndex); + QList<QGraphicsItem *> itemList; + QRectF exposeRect = polygon.boundingRect(); + _q_adjustRect(&exposeRect); + QPainterPath path; + path.addPolygon(polygon); + d->pathIntersector->scenePath = path; + d->items_helper(exposeRect, d->pathIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPainterPath + &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform + &deviceTransform) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a path and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a path are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine + the list to get an exact result. If you want to implement your own + refinement algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPainterPath &path, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsSceneIndex); + QList<QGraphicsItem *> itemList; + QRectF exposeRect = path.controlPointRect(); + _q_adjustRect(&exposeRect); + d->pathIntersector->scenePath = path; + d->items_helper(exposeRect, d->pathIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + This virtual function return an estimation of items at position \a point. + This method return a list sorted using \a order. +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::estimateItems(const QPointF &point, Qt::SortOrder order) const +{ + return estimateItems(QRectF(point, QSize(1, 1)), order); +} + +QList<QGraphicsItem *> QGraphicsSceneIndex::estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneIndex); + Q_UNUSED(rect); + QGraphicsScenePrivate *scened = d->scene->d_func(); + scened->ensureSortedTopLevelItems(); + if (order == Qt::AscendingOrder) { + QList<QGraphicsItem *> sorted; + for (int i = scened->topLevelItems.size() - 1; i >= 0; --i) + sorted << scened->topLevelItems.at(i); + return sorted; + } + return scened->topLevelItems; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const + + This pure virtual function all items in the index and sort them using + \a order. +*/ + + +/*! + Notifies the index that the scene's scene rect has changed. \a rect + is thew new scene rect. + + \sa QGraphicsScene::sceneRect() +*/ +void QGraphicsSceneIndex::updateSceneRect(const QRectF &rect) +{ + Q_UNUSED(rect); +} + +/*! + This virtual function removes all items in the scene index. +*/ +void QGraphicsSceneIndex::clear() +{ + const QList<QGraphicsItem *> allItems = items(); + for (int i = 0 ; i < allItems.size(); ++i) + removeItem(allItems.at(i)); +} + +/*! + \fn virtual void QGraphicsSceneIndex::addItem(QGraphicsItem *item) = 0 + + This pure virtual function inserts an \a item to the scene index. + + \sa removeItem(), deleteItem() +*/ + +/*! + \fn virtual void QGraphicsSceneIndex::removeItem(QGraphicsItem *item) = 0 + + This pure virtual function removes an \a item to the scene index. + + \sa addItem(), deleteItem() +*/ + +/*! + This method is called when an \a item has been deleted. + The default implementation call removeItem. Be carefull, + if your implementation of removeItem use pure virtual method + of QGraphicsItem like boundingRect(), then you should reimplement + this method. + + \sa addItem(), removeItem() +*/ +void QGraphicsSceneIndex::deleteItem(QGraphicsItem *item) +{ + removeItem(item); +} + +/*! + This virtual function is called by QGraphicsItem to notify the index + that some part of the \a item 's state changes. By reimplementing this + function, your can react to a change, and in some cases, (depending on \a + change,) adjustments in the index can be made. + + \a change is the parameter of the item that is changing. \a value is the + value that changed; the type of the value depends on \a change. + + The default implementation does nothing. + + \sa QGraphicsItem::GraphicsItemChange +*/ +void QGraphicsSceneIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value) +{ + Q_UNUSED(item); + Q_UNUSED(change); + Q_UNUSED(value); +} + +/*! + Notify the index for a geometry change of an \a item. + + \sa QGraphicsItem::prepareGeometryChange() +*/ +void QGraphicsSceneIndex::prepareBoundingRectChange(const QGraphicsItem *item) +{ + Q_UNUSED(item); +} + +QT_END_NAMESPACE + +#include "moc_qgraphicssceneindex_p.cpp" + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicssceneindex_p.h b/src/gui/graphicsview/qgraphicssceneindex_p.h new file mode 100644 index 0000000..37dffb8 --- /dev/null +++ b/src/gui/graphicsview/qgraphicssceneindex_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENEINDEX_H +#define QGRAPHICSSCENEINDEX_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicsscene_p.h" +#include "qgraphicsscene.h" +#include <private/qobject_p.h> + +#include <QtCore/qnamespace.h> +#include <QtCore/qobject.h> +#include <QtGui/qtransform.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsSceneIndexIntersector; +class QGraphicsSceneIndexPointIntersector; +class QGraphicsSceneIndexRectIntersector; +class QGraphicsSceneIndexPathIntersector; +class QGraphicsSceneIndexPrivate; +class QPointF; +class QRectF; +template<typename T> class QList; + +class Q_AUTOTEST_EXPORT QGraphicsSceneIndex : public QObject +{ + Q_OBJECT + +public: + QGraphicsSceneIndex(QGraphicsScene *scene = 0); + virtual ~QGraphicsSceneIndex(); + + QGraphicsScene *scene() const; + + virtual QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const = 0; + virtual QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> estimateItems(const QPointF &point, Qt::SortOrder order) const; + virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const = 0; + virtual QList<QGraphicsItem *> estimateTopLevelItems(const QRectF &, Qt::SortOrder order) const; + +protected Q_SLOTS: + virtual void updateSceneRect(const QRectF &rect); + +protected: + virtual void clear(); + virtual void addItem(QGraphicsItem *item) = 0; + virtual void removeItem(QGraphicsItem *item) = 0; + virtual void deleteItem(QGraphicsItem *item); + + virtual void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange, const QVariant &value); + virtual void prepareBoundingRectChange(const QGraphicsItem *item); + + QGraphicsSceneIndex(QGraphicsSceneIndexPrivate &dd, QGraphicsScene *scene); + + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QGraphicsSceneBspTreeIndex; +private: + Q_DISABLE_COPY(QGraphicsSceneIndex) + Q_DECLARE_PRIVATE(QGraphicsSceneIndex) +}; + +class QGraphicsSceneIndexPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneIndex) +public: + QGraphicsSceneIndexPrivate(QGraphicsScene *scene); + ~QGraphicsSceneIndexPrivate(); + + void init(); + static bool itemCollidesWithPath(const QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode); + + void recursive_items_helper(QGraphicsItem *item, QRectF exposeRect, + QGraphicsSceneIndexIntersector *intersector, QList<QGraphicsItem *> *items, + const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order, qreal parentOpacity = 1.0) const; + inline void items_helper(const QRectF &rect, QGraphicsSceneIndexIntersector *intersector, + QList<QGraphicsItem *> *items, const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order) const; + + QGraphicsScene *scene; + QGraphicsSceneIndexPointIntersector *pointIntersector; + QGraphicsSceneIndexRectIntersector *rectIntersector; + QGraphicsSceneIndexPathIntersector *pathIntersector; +}; + +inline void QGraphicsSceneIndexPrivate::items_helper(const QRectF &rect, QGraphicsSceneIndexIntersector *intersector, + QList<QGraphicsItem *> *items, const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order) const +{ + Q_Q(const QGraphicsSceneIndex); + const QList<QGraphicsItem *> tli = q->estimateTopLevelItems(rect, Qt::DescendingOrder); + for (int i = 0; i < tli.size(); ++i) + recursive_items_helper(tli.at(i), rect, intersector, items, viewTransform, mode, order); + if (order == Qt::AscendingOrder) { + const int n = items->size(); + for (int i = 0; i < n / 2; ++i) + items->swap(i, n - i - 1); + } +} + +class QGraphicsSceneIndexIntersector +{ +public: + QGraphicsSceneIndexIntersector() { } + virtual ~QGraphicsSceneIndexIntersector() { } + virtual bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const = 0; +}; + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSCENEINDEX_H diff --git a/src/gui/graphicsview/qgraphicsscenelinearindex.cpp b/src/gui/graphicsview/qgraphicsscenelinearindex.cpp new file mode 100644 index 0000000..52bbc79 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenelinearindex.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsSceneLinearIndex + \brief The QGraphicsSceneLinearIndex class provides an implementation of + a linear indexing algorithm for discovering items in QGraphicsScene. + \since 4.6 + \ingroup graphicsview-api + \internal + + QGraphicsSceneLinearIndex index is default linear implementation to discover items. + It basically store all items in a list and return them to the scene. + + \sa QGraphicsScene, QGraphicsView, QGraphicsSceneIndex, QGraphicsSceneBspTreeIndex +*/ + +#include <private/qgraphicsscenelinearindex_p.h> + +/*! + \fn QGraphicsSceneLinearIndex::QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0): + + Construct a linear index for the given \a scene. +*/ + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneLinearIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const; + + Return all items in the index and sort them using \a order. +*/ + + +/*! + \fn virtual QList<QGraphicsItem *> QGraphicsSceneLinearIndex::estimateItems(const QRectF &rect, Qt::SortOrder order) const + + Returns an estimation visible items that are either inside or + intersect with the specified \a rect and return a list sorted using \a order. +*/ + +/*! + \fn void QGraphicsSceneLinearIndex::clear() + \internal + Clear the all the BSP index. +*/ + +/*! + \fn virtual void QGraphicsSceneLinearIndex::addItem(QGraphicsItem *item) + + Add the \a item into the index. +*/ + +/*! + \fn virtual void QGraphicsSceneLinearIndex::removeItem(QGraphicsItem *item) + + Add the \a item from the index. +*/ + diff --git a/src/gui/graphicsview/qgraphicsscenelinearindex_p.h b/src/gui/graphicsview/qgraphicsscenelinearindex_p.h new file mode 100644 index 0000000..1f3d58b --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenelinearindex_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENELINEARINDEX_H +#define QGRAPHICSSCENELINEARINDEX_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include <QtCore/qrect.h> +#include <QtCore/qlist.h> +#include <QtGui/qgraphicsitem.h> +#include <private/qgraphicssceneindex_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_AUTOTEST_EXPORT QGraphicsSceneLinearIndex : public QGraphicsSceneIndex +{ + Q_OBJECT + +public: + QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0) : QGraphicsSceneIndex(scene) + { } + + QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const + { Q_UNUSED(order); return m_items; } + + virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const + { + Q_UNUSED(rect); + Q_UNUSED(order); + return m_items; + } + +protected : + virtual void clear() + { m_items.clear(); } + + virtual void addItem(QGraphicsItem *item) + { m_items << item; } + + virtual void removeItem(QGraphicsItem *item) + { m_items.removeOne(item); } + +private: + QList<QGraphicsItem*> m_items; +}; + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSCENELINEARINDEX_H diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp new file mode 100644 index 0000000..778cd94 --- /dev/null +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -0,0 +1,573 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsTransform + \brief The QGraphicsTransform class is an abstract base class for building + advanced transformations on QGraphicsItems. + \since 4.6 + + As an alternative to QGraphicsItem::transform, QGraphicsTransform lets you + create and control advanced transformations that can be configured + independently using specialized properties. + + QGraphicsItem allows you to assign any number of QGraphicsTransform + instances to one QGraphicsItem. Each QGraphicsTransform is applied in + order, one at a time, to the QGraphicsItem it's assigned to. + + QGraphicsTransform is particularily useful for animations. Whereas + QGraphicsItem::setTransform() lets you assign any transform directly to an + item, there is no direct way to interpolate between two different + transformations (e.g., when transitioning between two states, each for + which the item has a different arbitrary transform assigned). Using + QGraphicsTransform you can interpolate the property values of each + independent transformation. The resulting operation is then combined into a + single transform which is applied to QGraphicsItem. + + If you want to create your own configurable transformation, you can create + a subclass of QGraphicsTransform (or any or the existing subclasses), and + reimplement the pure virtual applyTo() function, which takes a pointer to a + QTransform. Each operation you would like to apply should be exposed as + properties (e.g., customTransform->setVerticalShear(2.5)). Inside you + reimplementation of applyTo(), you can modify the provided transform + respectively. + + QGraphicsTransform can be used together with QGraphicsItem::setTransform(), + QGraphicsItem::setRotation(), and QGraphicsItem::setScale(). + + \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation, QGraphicsRotation3D +*/ + +#include "qgraphicstransform.h" +#include "qgraphicsitem_p.h" +#include "qgraphicstransform_p.h" +#include <QDebug> + +#include <math.h> +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +QT_BEGIN_NAMESPACE + +void QGraphicsTransformPrivate::setItem(QGraphicsItem *i) +{ + if (item == i) + return; + + if (item) { + Q_Q(QGraphicsTransform); + QGraphicsItemPrivate *d_ptr = item->d_ptr; + + item->prepareGeometryChange(); + Q_ASSERT(d_ptr->transformData); + d_ptr->transformData->graphicsTransforms.removeAll(q); + d_ptr->dirtySceneTransform = 1; + item = 0; + } + + item = i; +} + +void QGraphicsTransformPrivate::updateItem(QGraphicsItem *item) +{ + item->prepareGeometryChange(); + item->d_ptr->dirtySceneTransform = 1; +} + +/*! + Constructs a new QGraphicsTransform with the given \a parent. +*/ +QGraphicsTransform::QGraphicsTransform(QObject *parent) + : QObject(*new QGraphicsTransformPrivate, parent) +{ +} + +/*! + Destroys the graphics transform. +*/ +QGraphicsTransform::~QGraphicsTransform() +{ + Q_D(QGraphicsTransform); + d->setItem(0); +} + +/*! + \internal +*/ +QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent) + : QObject(p, parent) +{ +} + +/*! + Applies this transformation to an identity transform, and returns the + resulting transform. + + This is equivalent to passing an identity transform to applyTo(). + + \sa applyTo() +*/ +QTransform QGraphicsTransform::transform() const +{ + QTransform t; + applyTo(&t); + return t; +} + +/*! + \fn void QGraphicsTransform::applyTo(QTransform *transform) const + + This pure virtual method has to be reimplemented in derived classes. + + It applies this transformation to \a transform. + + \sa QGraphicsItem::transform() +*/ + +/*! + Notifies that this transform operation has changed its parameters in such a + way that applyTo() will return a different result than before. + + When implementing you own custom graphics transform, you must call this + function every time you change a parameter, to let QGraphicsItem know that + its transformation needs to be updated. + + \sa applyTo() +*/ +void QGraphicsTransform::update() +{ + Q_D(QGraphicsTransform); + if (d->item) + d->updateItem(d->item); +} + +/*! + \class QGraphicsScale + \brief The QGraphicsScale class provides a scale transformation. + \since 4.6 + + QGraphicsScene provides certain parameters to help control how the scale + should be applied. + + The origin is the point that the item is scaled from (i.e., it stays fixed + relative to the parent as the rest of the item grows). By default the + origin is QPointF(0, 0). + + The two parameters xScale and yScale describe the scale factors to apply in + horizontal and vertical direction. They can take on any value, including 0 + (to collapse the item to a point) or negativate value. A negative xScale + value will mirror the item horizontally. A negative yScale value will flip + the item vertically. + + \sa QGraphicsTransform, QGraphicsItem::setScale(), QTransform::scale() +*/ + +class QGraphicsScalePrivate : public QGraphicsTransformPrivate +{ +public: + QGraphicsScalePrivate() + : xScale(1), yScale(1) {} + QPointF origin; + qreal xScale; + qreal yScale; +}; + +/*! + Constructs an empty QGraphicsScale object with the given \a parent. +*/ +QGraphicsScale::QGraphicsScale(QObject *parent) + : QGraphicsTransform(*new QGraphicsScalePrivate, parent) +{ +} + +/*! + Destroys the graphics scale. +*/ +QGraphicsScale::~QGraphicsScale() +{ +} + +/*! + \property QGraphicsScale::origin + \brief The QGraphicsScene class provides the origin of the scale. + + All scaling will be done relative to this point (i.e., this point + will stay fixed, relative to the parent, when the item is scaled). + + \sa xScale, yScale +*/ +QPointF QGraphicsScale::origin() const +{ + Q_D(const QGraphicsScale); + return d->origin; +} +void QGraphicsScale::setOrigin(const QPointF &point) +{ + Q_D(QGraphicsScale); + d->origin = point; + update(); + emit originChanged(); +} + +/*! + \property QGraphicsScale::xScale + \brief the horizontal scale factor. + + The scale factor can be any real number; the default value is 1.0. If you + set the factor to 0.0, the item will be collapsed to a single point. If you + provide a negative value, the item will be mirrored horizontally around its + origin. + + \sa yScale, origin +*/ +qreal QGraphicsScale::xScale() const +{ + Q_D(const QGraphicsScale); + return d->xScale; +} +void QGraphicsScale::setXScale(qreal scale) +{ + Q_D(QGraphicsScale); + if (d->xScale == scale) + return; + d->xScale = scale; + update(); + emit scaleChanged(); +} + +/*! + \property QGraphicsScale::yScale + \brief the vertical scale factor. + + The scale factor can be any real number; the default value is 1.0. If you + set the factor to 0.0, the item will be collapsed to a single point. If you + provide a negative value, the item will be flipped vertically around its + origin. + + \sa xScale, origin +*/ +qreal QGraphicsScale::yScale() const +{ + Q_D(const QGraphicsScale); + return d->yScale; +} +void QGraphicsScale::setYScale(qreal scale) +{ + Q_D(QGraphicsScale); + if (d->yScale == scale) + return; + d->yScale = scale; + update(); + emit scaleChanged(); +} + +/*! + \reimp +*/ +void QGraphicsScale::applyTo(QTransform *transform) const +{ + Q_D(const QGraphicsScale); + transform->translate(d->origin.x(), d->origin.y()); + transform->scale(d->xScale, d->yScale); + transform->translate(-d->origin.x(), -d->origin.y()); +} + +/*! + \fn QGraphicsScale::originChanged() + + QGraphicsScale emits this signal when its origin changes. + + \sa QGraphicsScale::origin +*/ + +/*! + \fn QGraphicsScale::scaleChanged() + + This signal is emitted whenever the xScale or yScale of the object + changes. + + \sa QGraphicsScale::xScale, QGraphicsScale::yScale +*/ + +/*! + \class QGraphicsRotation + \brief The QGraphicsRotation class provides a rotation transformation. + \since 4.6 + + QGraphicsRotation provides certain parameters to help control how the + rotation should be applied. + + The origin is the point that the item is rotated around (i.e., it stays + fixed relative to the parent as the rest of the item is rotated). By + default the origin is QPointF(0, 0). + + The angle property provides the number of degrees to rotate the item + clockwise around the origin. This value also be negative, indicating a + counter-clockwise rotation. For animation purposes it may also be useful to + provide rotation angles exceeding (-360, 360) degrees, for instance to + animate how an item rotates several times. + + \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() +*/ + +class QGraphicsRotationPrivate : public QGraphicsTransformPrivate +{ +public: + QGraphicsRotationPrivate() + : angle(0) {} + QPointF origin; + qreal originY; + qreal angle; +}; + +/*! + Constructs a new QGraphicsRotation with the given \a parent. +*/ +QGraphicsRotation::QGraphicsRotation(QObject *parent) + : QGraphicsTransform(*new QGraphicsRotationPrivate, parent) +{ +} + +/*! + \internal +*/ +QGraphicsRotation::QGraphicsRotation(QGraphicsRotationPrivate &p, QObject *parent) + : QGraphicsTransform(p, parent) +{ +} + +/*! + Destroys the graphics rotation. +*/ +QGraphicsRotation::~QGraphicsRotation() +{ +} + +/*! + \property QGraphicsRotation::origin + \brief the origin of the rotation. + + All rotations will be done relative to this point (i.e., this point + will stay fixed, relative to the parent, when the item is rotated). + + \sa angle +*/ +QPointF QGraphicsRotation::origin() const +{ + Q_D(const QGraphicsRotation); + return d->origin; +} +void QGraphicsRotation::setOrigin(const QPointF &point) +{ + Q_D(QGraphicsRotation); + d->origin = point; + update(); + emit originChanged(); +} + +/*! + \property QGraphicsRotation::angle + \brief the angle for clockwise rotation, in degrees. + + The angle can be any real number; the default value is 0.0. A value of 180 + will rotate 180 degrees, clockwise. If you provide a negative number, the + item will be rotated counter-clockwise. Normally the rotation angle will be + in the range (-360, 360), but you can also provide numbers outside of this + range (e.g., a angle of 370 degrees gives the same result as 10 degrees). + + \sa origin +*/ +qreal QGraphicsRotation::angle() const +{ + Q_D(const QGraphicsRotation); + return d->angle; +} +void QGraphicsRotation::setAngle(qreal angle) +{ + Q_D(QGraphicsRotation); + if (d->angle == angle) + return; + d->angle = angle; + update(); + emit angleChanged(); +} + +/*! + \reimp +*/ +void QGraphicsRotation::applyTo(QTransform *t) const +{ + Q_D(const QGraphicsRotation); + if (d->angle) { + t->translate(d->origin.x(), d->origin.y()); + t->rotate(d->angle); + t->translate(-d->origin.x(), -d->origin.y()); + } +} + +/*! + \fn QGraphicsRotation::originChanged() + + This signal is emitted whenever the origin has changed. + + \sa QGraphicsRotation::origin +*/ + +/*! + \fn void QGraphicsRotation::angleChanged() + + This signal is emitted whenever the angle has changed. + + \sa QGraphicsRotation::angle +*/ + +/*! + \class QGraphicsRotation3D + \brief The QGraphicsRotation3D class provides rotation in 3 dimensions. + \since 4.6 + + QGraphicsRotation3D extends QGraphicsRotation with the ability to rotate + around a given axis. + + You can provide the desired axis by assigning a QVector3D to the axis + property. The angle property, which is provided by QGraphicsRotation, now + describes the number of degrees to rotate around this axis. + + By default the axis is (0, 0, 1), giving QGraphicsRotation3D the same + default behavior as QGraphicsRotation (i.e., rotation around the Z axis). + + \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() +*/ + +class QGraphicsRotation3DPrivate : public QGraphicsRotationPrivate +{ +public: + QGraphicsRotation3DPrivate() {} + + QVector3D axis; +}; + +/*! + Constructs a new QGraphicsRotation3D with the given \a parent. +*/ +QGraphicsRotation3D::QGraphicsRotation3D(QObject *parent) + : QGraphicsRotation(*new QGraphicsRotation3DPrivate, parent) +{ +} + +/*! + Destroys the 3D graphics rotation. +*/ +QGraphicsRotation3D::~QGraphicsRotation3D() +{ +} + +/*! + \property QGraphicsRotation3D::axis + \brief a rotation axis, specified by a vector in 3D space. + + This can be any axis in 3D space. By default the axis is (0, 0, 1), + which is aligned with the Z axis and provides the same behavior + for the rotation angle as QGraphicsRotation. If you provide another + axis, QGraphicsRotation3D will provide a transformation that rotates + around this axis. For example, if you would like to rotate an item + around its X axis, you could pass (1, 0, 0) as the axis. + + \sa QTransform, QGraphicsRotation::angle +*/ +QVector3D QGraphicsRotation3D::axis() +{ + Q_D(QGraphicsRotation3D); + return d->axis; +} +void QGraphicsRotation3D::setAxis(const QVector3D &axis) +{ + Q_D(QGraphicsRotation3D); + d->axis = axis; + update(); +} + +static const qreal inv_dist_to_plane = 1. / 1024.; + +/*! + \reimp +*/ +void QGraphicsRotation3D::applyTo(QTransform *t) const +{ + Q_D(const QGraphicsRotation3D); + + if (d->angle == 0. || + (d->axis.z() == 0. && d->axis.y() == 0 && d->axis.x() == 0)) + return; + + qreal rad = d->angle * 2. * M_PI / 360.; + qreal c = ::cos(rad); + qreal s = ::sin(rad); + + qreal x = d->axis.x(); + qreal y = d->axis.y(); + qreal z = d->axis.z(); + + qreal len = x * x + y * y + z * z; + if (len != 1.) { + len = 1./::sqrt(len); + x *= len; + y *= len; + z *= len; + } + + t->translate(d->origin.x(), d->origin.y()); + *t = QTransform(x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s*inv_dist_to_plane, + y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane, + 0, 0, 1) * *t; + t->translate(-d->origin.x(), -d->origin.y()); +} + +/*! + \fn void QGraphicsRotation3D::axisChanged() + + This signal is emitted whenever the axis of the object changes. +*/ + +#include "moc_qgraphicstransform.cpp" + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h new file mode 100644 index 0000000..d7c07d0 --- /dev/null +++ b/src/gui/graphicsview/qgraphicstransform.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSTRANSFORM_H +#define QGRAPHICSTRANSFORM_H + +#include <QtCore/QObject> +#include <QtGui/QTransform> +#include <QtGui/QVector3D> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGraphicsItem; +class QGraphicsTransformPrivate; + +class Q_GUI_EXPORT QGraphicsTransform : public QObject +{ + Q_OBJECT +public: + QGraphicsTransform(QObject *parent = 0); + ~QGraphicsTransform(); + + QTransform transform() const; + virtual void applyTo(QTransform *transform) const = 0; + +protected Q_SLOTS: + void update(); + +protected: + QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent); + +private: + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + Q_DECLARE_PRIVATE(QGraphicsTransform) +}; + +class QGraphicsScalePrivate; + +class Q_GUI_EXPORT QGraphicsScale : public QGraphicsTransform +{ + Q_OBJECT + + Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY scaleChanged) + Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY scaleChanged) +public: + QGraphicsScale(QObject *parent = 0); + ~QGraphicsScale(); + + QPointF origin() const; + void setOrigin(const QPointF &point); + + qreal xScale() const; + void setXScale(qreal); + + qreal yScale() const; + void setYScale(qreal); + + void applyTo(QTransform *transform) const; + +Q_SIGNALS: + void originChanged(); + void scaleChanged(); + +private: + Q_DECLARE_PRIVATE(QGraphicsScale) +}; + +class QGraphicsRotationPrivate; + +class Q_GUI_EXPORT QGraphicsRotation : public QGraphicsTransform +{ + Q_OBJECT + + Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) +public: + QGraphicsRotation(QObject *parent = 0); + ~QGraphicsRotation(); + + QPointF origin() const; + void setOrigin(const QPointF &point); + + qreal angle() const; + void setAngle(qreal); + + void applyTo(QTransform *transform) const; + +Q_SIGNALS: + void originChanged(); + void angleChanged(); + +protected: + QGraphicsRotation(QGraphicsRotationPrivate &p, QObject *parent); +private: + Q_DECLARE_PRIVATE(QGraphicsRotation) +}; + +class QGraphicsRotation3DPrivate; + +class Q_GUI_EXPORT QGraphicsRotation3D : public QGraphicsRotation +{ + Q_OBJECT + + Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) +public: + QGraphicsRotation3D(QObject *parent = 0); + ~QGraphicsRotation3D(); + + QVector3D axis(); + void setAxis(const QVector3D &axis); + + void applyTo(QTransform *transform) const; + +Q_SIGNALS: + void axisChanged(); + +private: + Q_DECLARE_PRIVATE(QGraphicsRotation3D) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXTRANSFORM_H diff --git a/src/gui/graphicsview/qgraphicstransform_p.h b/src/gui/graphicsview/qgraphicstransform_p.h new file mode 100644 index 0000000..2d36eda --- /dev/null +++ b/src/gui/graphicsview/qgraphicstransform_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSTRANSFORM_P_H +#define QGRAPHICSTRANSFORM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qobject_p.h" + +class QGraphicsItem; + +class QGraphicsTransformPrivate : public QObjectPrivate { +public: + Q_DECLARE_PUBLIC(QGraphicsTransform) + + QGraphicsTransformPrivate() + : QObjectPrivate(), item(0) {} + + QGraphicsItem *item; + + void setItem(QGraphicsItem *item); + static void updateItem(QGraphicsItem *item); +}; + +#endif // QGRAPHICSTRANSFORM_P_H diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index b888b01..ca55f2e 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -39,8 +39,6 @@ ** ****************************************************************************/ -//#define QGRAPHICSVIEW_DEBUG - static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50; static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 2^9 @@ -99,8 +97,8 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport widget. - QGraphicsView supports affine transformations, using QMatrix. You can - either pass a matrix to setMatrix(), or you can call one of the + QGraphicsView supports affine transformations, using QTransform. You can + either pass a matrix to setTransform(), or you can call one of the convenience functions rotate(), scale(), translate() or shear(). The most two common transformations are scaling, which is used to implement zooming, and rotation. QGraphicsView keeps the center of the view fixed @@ -200,16 +198,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < Note that setting a flag usually imposes a side effect, and this effect can vary between paint devices and platforms. - \value DontClipPainter QGraphicsView sometimes clips the painter when - rendering the scene contents. This can generally improve performance - (e.g., rendering only small parts of a large pixmap), and protects against - rendering mistakes (e.g., drawing outside bounding rectangles, or outside - the exposed area). In some situations, however, the painter clip can slow - down rendering; especially when all painting is restricted to inside - exposed areas. By enabling this flag, QGraphicsView will completely - disable its implicit clipping. Note that rendering artifacts from using a - semi-transparent foreground or background brush can occur if clipping is - disabled. + \value DontClipPainter This value is obsolete and has no effect. \value DontSavePainterState When rendering, QGraphicsView protects the painter state (see QPainter::save()) when rendering the background or @@ -228,6 +217,8 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < minimizing the areas that require redrawing, which improves performance. A common side effect is that items that do draw with antialiasing can leave painting traces behind on the scene as they are moved. + + \omitvalue IndirectPainting */ /*! @@ -289,12 +280,17 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < #include <QtGui/qpainter.h> #include <QtGui/qscrollbar.h> #include <QtGui/qstyleoption.h> +#include <QtGui/qinputcontext.h> #ifdef Q_WS_X11 #include <private/qt_x11_p.h> #endif +#include <private/qevent_p.h> + QT_BEGIN_NAMESPACE +bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); + inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision { if (d <= (qreal) INT_MIN) @@ -304,6 +300,23 @@ inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for sing return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1); } +void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent) +{ + QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + // the scene will set the item local pos, startPos, lastPos, and rect before delivering to + // an item, but for now those functions are returning the view's local coordinates + touchPoint.setSceneRect(d->mapToScene(touchPoint.rect())); + touchPoint.setStartScenePos(d->mapToScene(touchPoint.startPos())); + touchPoint.setLastScenePos(d->mapToScene(touchPoint.lastPos())); + + // screenPos, startScreenPos, lastScreenPos, and screenRect are already set + } + + touchEvent->setTouchPoints(touchPoints); +} + /*! \internal */ @@ -338,8 +351,6 @@ QGraphicsViewPrivate::QGraphicsViewPrivate() #endif lastDragDropEvent(0), fullUpdatePending(true), - dirtyRectCount(0), - updatingLater(false), updateSceneSlotReimplementedChecked(false) { styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS); @@ -400,7 +411,7 @@ void QGraphicsViewPrivate::recalculateContentSize() int left = q_round_bound(viewRect.left()); int right = q_round_bound(viewRect.right() - width); if (left >= right) { - q->horizontalScrollBar()->setRange(0, 0); + hbar->setRange(0, 0); switch (alignment & Qt::AlignHorizontal_Mask) { case Qt::AlignLeft: @@ -415,9 +426,9 @@ void QGraphicsViewPrivate::recalculateContentSize() break; } } else { - q->horizontalScrollBar()->setRange(left, right); - q->horizontalScrollBar()->setPageStep(width); - q->horizontalScrollBar()->setSingleStep(width / 20); + hbar->setRange(left, right); + hbar->setPageStep(width); + hbar->setSingleStep(width / 20); leftIndent = 0; } @@ -426,7 +437,7 @@ void QGraphicsViewPrivate::recalculateContentSize() int top = q_round_bound(viewRect.top()); int bottom = q_round_bound(viewRect.bottom() - height); if (top >= bottom) { - q->verticalScrollBar()->setRange(0, 0); + vbar->setRange(0, 0); switch (alignment & Qt::AlignVertical_Mask) { case Qt::AlignTop: @@ -441,9 +452,9 @@ void QGraphicsViewPrivate::recalculateContentSize() break; } } else { - q->verticalScrollBar()->setRange(top, bottom); - q->verticalScrollBar()->setPageStep(height); - q->verticalScrollBar()->setSingleStep(height / 20); + vbar->setRange(top, bottom); + vbar->setPageStep(height); + vbar->setSingleStep(height / 20); topIndent = 0; } @@ -455,7 +466,7 @@ void QGraphicsViewPrivate::recalculateContentSize() // scroll instead. if (oldLeftIndent != leftIndent || oldTopIndent != topIndent) { dirtyScroll = true; - q->viewport()->update(); + updateAll(); } else if (q->isRightToLeft() && !leftIndent) { // In reverse mode, the horizontal scroll always changes after the content // size has changed, as the scroll is calculated by summing the min and @@ -481,7 +492,7 @@ void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor) if (q->underMouse()) { // Last scene pos: lastMouseMoveScenePoint // Current mouse pos: - QPointF transformationDiff = q->mapToScene(q->viewport()->rect().center()) + QPointF transformationDiff = q->mapToScene(viewport->rect().center()) - q->mapToScene(q->mapFromGlobal(QCursor::pos())); q->centerOn(lastMouseMoveScenePoint + transformationDiff);; } else { @@ -503,7 +514,7 @@ void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor) void QGraphicsViewPrivate::updateLastCenterPoint() { Q_Q(QGraphicsView); - lastCenterPoint = q->mapToScene(q->viewport()->rect().center()); + lastCenterPoint = q->mapToScene(viewport->rect().center()); } /*! @@ -541,15 +552,15 @@ void QGraphicsViewPrivate::updateScroll() scrollX = qint64(-leftIndent); if (q->isRightToLeft()) { if (!leftIndent) { - scrollX += q->horizontalScrollBar()->minimum(); - scrollX += q->horizontalScrollBar()->maximum(); - scrollX -= q->horizontalScrollBar()->value(); + scrollX += hbar->minimum(); + scrollX += hbar->maximum(); + scrollX -= hbar->value(); } } else { - scrollX += q->horizontalScrollBar()->value(); + scrollX += hbar->value(); } - scrollY = qint64(q->verticalScrollBar()->value() - topIndent); + scrollY = qint64(vbar->value() - topIndent); dirtyScroll = false; } @@ -589,7 +600,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) return; QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); - mouseEvent.setWidget(q->viewport()); + mouseEvent.setWidget(viewport); mouseEvent.setButtonDownScenePos(mousePressButton, mousePressScenePoint); mouseEvent.setButtonDownScreenPos(mousePressButton, mousePressScreenPoint); mouseEvent.setScenePos(q->mapToScene(event->pos())); @@ -602,7 +613,10 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) lastMouseMoveScenePoint = mouseEvent.scenePos(); lastMouseMoveScreenPoint = mouseEvent.screenPos(); mouseEvent.setAccepted(false); - QApplication::sendEvent(scene, &mouseEvent); + if (event->spontaneous()) + qt_sendSpontaneousEvent(scene, &mouseEvent); + else + QApplication::sendEvent(scene, &mouseEvent); // Remember whether the last event was accepted or not. lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -614,6 +628,16 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) } #ifndef QT_NO_CURSOR + // If all the items ignore hover events, we don't look-up any items + // in QGraphicsScenePrivate::dispatchHoverEvent, hence the + // cachedItemsUnderMouse list will be empty. We therefore do the look-up + // for cursor items here if not all items use the default cursor. + if (scene->d_func()->allItemsIgnoreHoverEvents && !scene->d_func()->allItemsUseDefaultCursor + && scene->d_func()->cachedItemsUnderMouse.isEmpty()) { + scene->d_func()->cachedItemsUnderMouse = scene->d_func()->itemsAtPosition(mouseEvent.screenPos(), + mouseEvent.scenePos(), + mouseEvent.widget()); + } // Find the topmost item under the mouse with a cursor. foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) { if (item->hasCursor()) { @@ -626,7 +650,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) if (hasStoredOriginalCursor) { // Restore the original viewport cursor. hasStoredOriginalCursor = false; - q->viewport()->setCursor(originalCursor); + viewport->setCursor(originalCursor); } #endif } @@ -658,8 +682,6 @@ QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRec #ifndef QT_NO_CURSOR void QGraphicsViewPrivate::_q_setViewportCursor(const QCursor &cursor) { - Q_Q(QGraphicsView); - QWidget *viewport = q->viewport(); if (!hasStoredOriginalCursor) { hasStoredOriginalCursor = true; originalCursor = viewport->cursor(); @@ -685,9 +707,9 @@ void QGraphicsViewPrivate::_q_unsetViewportCursor() // Restore the original viewport cursor. hasStoredOriginalCursor = false; if (dragMode == QGraphicsView::ScrollHandDrag) - q->viewport()->setCursor(Qt::OpenHandCursor); + viewport->setCursor(Qt::OpenHandCursor); else - q->viewport()->setCursor(originalCursor); + viewport->setCursor(originalCursor); } #endif @@ -726,7 +748,7 @@ void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEven dest->setProposedAction(source->proposedAction()); dest->setDropAction(source->dropAction()); dest->setMimeData(source->mimeData()); - dest->setWidget(q->viewport()); + dest->setWidget(viewport); dest->setSource(source->source()); #else Q_UNUSED(dest) @@ -749,12 +771,13 @@ QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRect } // Translate-only + // COMBINE QPointF offset; const QGraphicsItem *parentItem = item; const QGraphicsItemPrivate *itemd; do { itemd = parentItem->d_ptr; - if (itemd->hasTransform) + if (itemd->transformData) break; offset += itemd->pos; } while ((parentItem = itemd->parent)); @@ -786,241 +809,120 @@ QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const Q const_cast<QGraphicsViewPrivate *>(this)->updateScroll(); // Accurate bounding region - QTransform itv = item->sceneTransform() * q->viewportTransform(); + QTransform itv = item->deviceTransform(q->viewportTransform()); return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect(); } -// QRectF::intersects() returns false always if either the source or target -// rectangle's width or height are 0. This works around that problem. -static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) -{ - Q_ASSERT(item); - QRectF boundingRect(item->boundingRect()); - if (!boundingRect.width()) - boundingRect.adjust(-0.00001, 0, 0.00001, 0); - if (!boundingRect.height()) - boundingRect.adjust(0, -0.00001, 0, 0.00001); - return boundingRect; -} - /*! \internal */ -void QGraphicsViewPrivate::itemUpdated(QGraphicsItem *item, const QRectF &rect) +void QGraphicsViewPrivate::processPendingUpdates() { - if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate) - return; - if (item->d_ptr->dirty) - updateLater(); - - QRectF updateRect = rect; - if ((item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) || item->d_ptr->children.isEmpty()) { - updateRect &= adjustedItemBoundingRect(item); - if (updateRect.isEmpty()) - return; - } - - QGraphicsItem *clipItem = item; - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { - // Minimize unnecessary redraw. - QGraphicsItem *parent = item; - while ((parent = parent->d_ptr->parent)) { - if (parent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) { - // Map update rect to the current parent and itersect with its bounding rect. - updateRect = clipItem->itemTransform(parent).mapRect(updateRect) - & adjustedItemBoundingRect(parent); - if (updateRect.isEmpty()) - return; - clipItem = parent; - } - - if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) - break; - } - } - - // Map update rect from clipItem coordinates to view coordinates. - Q_ASSERT(clipItem); - if (!item->d_ptr->hasBoundingRegionGranularity) - this->updateRect(mapToViewRect(clipItem, updateRect) & viewport->rect()); - else - updateRegion(mapToViewRegion(clipItem, updateRect) & viewport->rect()); -} - -void QGraphicsViewPrivate::updateLater() -{ - Q_Q(QGraphicsView); - if (updatingLater) - return; - updatingLater = true; - QMetaObject::invokeMethod(q, "_q_updateLaterSlot", Qt::QueuedConnection); -} - -void QGraphicsViewPrivate::_q_updateLaterSlot() -{ - Q_Q(QGraphicsView); if (!scene) return; - QRect vr = viewport->rect(); - QTransform viewTransform = q->viewportTransform(); - const QList<QGraphicsItem *> &dirtyItems = scene->d_func()->dirtyItems; - for (int i = 0; i < dirtyItems.size(); ++i) { - const QGraphicsItem *item = dirtyItems.at(i); - if (item->d_ptr->discardUpdateRequest(/*ignoreClipping=*/false, - /*ignoreVisibleBit=*/false, - /*ignoreDirtyBit=*/true)) { - continue; - } - QTransform x = item->sceneTransform() * viewTransform; - updateRect(x.mapRect(item->boundingRect()).toAlignedRect() & vr); - } - - dirtyRectCount += dirtyRects.size(); - - bool noUpdate = !fullUpdatePending && viewportUpdateMode == QGraphicsView::FullViewportUpdate; - if ((dirtyRectCount > 0 || !dirtyBoundingRect.isEmpty()) && !fullUpdatePending && !noUpdate) { - if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate - || (viewportUpdateMode == QGraphicsView::SmartViewportUpdate - && dirtyRectCount >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD)) { - if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) { - viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2)); - } else { - viewport->update(dirtyBoundingRect); - } - } else { - // ### Improve this block, which is very slow for complex regions. We - // need to strike the balance between having an accurate update - // region, and running fast. The below approach is the simplest way to - // create a region from a bunch of rects, but we might want to use - // other approaches; e.g., a grid of a fixed size representing - // quadrants of the viewport, which we mark as dirty depending on the - // rectangles in the list. Perhaps this should go into a - // QRegion::fromRects(rects, how) function. - QRegion region; - if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) { - for (int i = 0; i < dirtyRegions.size(); ++i) { - QVector<QRect> rects = dirtyRegions.at(i).rects(); - for (int j = 0; j < rects.size(); ++j) - region += rects.at(j).adjusted(-2, -2, 2, 2); - } - for (int i = 0; i < dirtyRects.size(); ++i) - region += dirtyRects.at(i).adjusted(-2, -2, 2, 2); - } else { - for (int i = 0; i < dirtyRegions.size(); ++i) - region += dirtyRegions.at(i); - for (int i = 0; i < dirtyRects.size(); ++i) - region += dirtyRects.at(i); - } - - viewport->update(region); - } + if (fullUpdatePending) { + viewport->update(); + } else if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) { + if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) + viewport->update(dirtyBoundingRect.adjusted(-1, -1, 1, 1)); + else + viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2)); + } else { + viewport->update(dirtyRegion); // Already adjusted in updateRect/Region. } - dirtyRegions.clear(); - dirtyRects.clear(); - dirtyRectCount = 0; dirtyBoundingRect = QRect(); - updatingLater = false; + dirtyRegion = QRegion(); } -void QGraphicsViewPrivate::updateAll() +static inline bool intersectsViewport(const QRect &r, int width, int height) +{ return !(r.left() > width) && !(r.right() < 0) && !(r.top() >= height) && !(r.bottom() < 0); } + +static inline bool containsViewport(const QRect &r, int width, int height) +{ return r.left() <= 0 && r.top() <= 0 && r.right() >= width - 1 && r.bottom() >= height - 1; } + +static inline void QRect_unite(QRect *rect, const QRect &other) { - Q_Q(QGraphicsView); - q->viewport()->update(); - fullUpdatePending = true; - dirtyRectCount = 0; - dirtyBoundingRect = QRect(); - updatingLater = false; + if (rect->isEmpty()) { + *rect = other; + } else { + rect->setCoords(qMin(rect->left(), other.left()), qMin(rect->top(), other.top()), + qMax(rect->right(), other.right()), qMax(rect->bottom(), other.bottom())); + } } -void QGraphicsViewPrivate::updateRegion(const QRegion &r) +bool QGraphicsViewPrivate::updateRegion(const QRegion &r) { - if (r.isEmpty()) - return; + if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate || r.isEmpty()) + return false; - Q_Q(QGraphicsView); + const QRect boundingRect = r.boundingRect(); + if (!intersectsViewport(boundingRect, viewport->width(), viewport->height())) + return false; // Update region outside viewport. - // Rect intersects viewport - update everything? switch (viewportUpdateMode) { case QGraphicsView::FullViewportUpdate: fullUpdatePending = true; - q->viewport()->update(); + viewport->update(); break; case QGraphicsView::BoundingRectViewportUpdate: - dirtyBoundingRect |= r.boundingRect(); - if (dirtyBoundingRect == q->viewport()->rect()) { + QRect_unite(&dirtyBoundingRect, boundingRect); + if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) { fullUpdatePending = true; - q->viewport()->update(); - } else { - updateLater(); + viewport->update(); } break; - case QGraphicsView::SmartViewportUpdate: - dirtyBoundingRect |= r.boundingRect(); - if ((dirtyRectCount + r.numRects()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD) - dirtyRegions << r; - dirtyRectCount += r.numRects(); - updateLater(); - break; + case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE case QGraphicsView::MinimalViewportUpdate: - dirtyRegions << r; - dirtyRectCount += r.numRects(); - updateLater(); + { + const QVector<QRect> &rects = r.rects(); + for (int i = 0; i < rects.size(); ++i) { + if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) + dirtyRegion += rects.at(i).adjusted(-1, -1, 1, 1); + else + dirtyRegion += rects.at(i).adjusted(-2, -2, 2, 2); + } break; - case QGraphicsView::NoViewportUpdate: - // Unreachable + } + default: break; } - // Compress the regions... - if (dirtyRectCount > QGRAPHICSVIEW_REGION_RECT_THRESHOLD && dirtyRegions.size() > 1) { - QRegion masterRegion; - for (int i=0; i<dirtyRegions.size(); ++i) { - masterRegion |= dirtyRegions.at(i); - } - dirtyRectCount = masterRegion.numRects(); - dirtyRegions.clear(); - dirtyRegions << masterRegion; - } + return true; } -void QGraphicsViewPrivate::updateRect(const QRect &r) +bool QGraphicsViewPrivate::updateRect(const QRect &r) { - if (r.isEmpty()) - return; - - Q_Q(QGraphicsView); + if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate + || !intersectsViewport(r, viewport->width(), viewport->height())) { + return false; + } - // Rect intersects viewport - update everything? switch (viewportUpdateMode) { case QGraphicsView::FullViewportUpdate: fullUpdatePending = true; - q->viewport()->update(); + viewport->update(); break; case QGraphicsView::BoundingRectViewportUpdate: - dirtyBoundingRect |= r; - if (dirtyBoundingRect == q->viewport()->rect()) { + QRect_unite(&dirtyBoundingRect, r); + if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) { fullUpdatePending = true; - q->viewport()->update(); - } else { - updateLater(); + viewport->update(); } break; - case QGraphicsView::SmartViewportUpdate: - dirtyBoundingRect |= r; - if ((dirtyRectCount + dirtyRects.size()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD) - dirtyRects << r; - updateLater(); - break; + case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE case QGraphicsView::MinimalViewportUpdate: - dirtyRects << r; - updateLater(); + if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) + dirtyRegion += r.adjusted(-1, -1, 1, 1); + else + dirtyRegion += r.adjusted(-2, -2, 2, 2); break; - case QGraphicsView::NoViewportUpdate: - // Unreachable + default: break; } + + return true; } QStyleOptionGraphicsItem *QGraphicsViewPrivate::allocStyleOptionsArray(int numItems) @@ -1055,47 +957,32 @@ extern QPainterPath qt_regionToPath(const QRegion ®ion); is at risk of painting 1 pixel outside the bounding rect. Therefore we must search for items with an adjustment of (-1, -1, 1, 1). */ -QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems) const +QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems, + const QTransform &viewTransform) const { Q_Q(const QGraphicsView); // Step 1) If all items are contained within the expose region, then - // return a list of all visible items. - const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 2, 2)) + // return a list of all visible items. ### the scene's growing bounding + // rect does not take into account untransformable items. + const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 1, 1)) .boundingRect(); - if (exposedRegionSceneBounds.contains(scene->d_func()->growingItemsBoundingRect)) { + if (exposedRegionSceneBounds.contains(scene->sceneRect())) { Q_ASSERT(allItems); *allItems = true; - // All items are guaranteed within the exposed region, don't bother using the index. - QList<QGraphicsItem *> itemList(scene->items()); - int i = 0; - while (i < itemList.size()) { - const QGraphicsItem *item = itemList.at(i); - // But we only want to include items that are visible - // The following check is basically the same as item->d_ptr->isInvisible(), except - // that we don't check whether the item clips children to shape or propagates its - // opacity (we loop through all items, so those checks are wrong in this context). - if (!item->isVisible() || item->d_ptr->isClippedAway() || item->d_ptr->isFullyTransparent()) - itemList.removeAt(i); - else - ++i; - } - - // Sort the items. - QGraphicsScenePrivate::sortItems(&itemList, Qt::DescendingOrder, scene->d_func()->sortCacheEnabled); - return itemList; + // All items are guaranteed within the exposed region. + return scene->items(Qt::DescendingOrder); } // Step 2) If the expose region is a simple rect and the view is only // translated or scaled, search for items using // QGraphicsScene::items(QRectF). - bool simpleRectLookup = (scene->d_func()->largestUntransformableItem.isNull() - && exposedRegion.numRects() == 1 && matrix.type() <= QTransform::TxScale); + bool simpleRectLookup = exposedRegion.numRects() == 1 && matrix.type() <= QTransform::TxScale; if (simpleRectLookup) { - return scene->d_func()->items_helper(exposedRegionSceneBounds, - Qt::IntersectsItemBoundingRect, - Qt::DescendingOrder); + return scene->items(exposedRegionSceneBounds, + Qt::IntersectsItemBoundingRect, + Qt::DescendingOrder, viewTransform); } // If the region is complex or the view has a complex transform, adjust @@ -1105,79 +992,25 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg foreach (const QRect &r, exposedRegion.rects()) adjustedRegion += r.adjusted(-1, -1, 1, 1); - const QPainterPath exposedPath(qt_regionToPath(adjustedRegion)); - if (scene->d_func()->largestUntransformableItem.isNull()) { - const QPainterPath exposedScenePath(q->mapToScene(exposedPath)); - return scene->d_func()->items_helper(exposedScenePath, - Qt::IntersectsItemBoundingRect, - Qt::DescendingOrder); - } - - // NB! Path must be in viewport coordinates. - return itemsInArea(exposedPath, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder); -} - -void QGraphicsViewPrivate::generateStyleOptions(const QList<QGraphicsItem *> &itemList, - QGraphicsItem **itemArray, - QStyleOptionGraphicsItem *styleOptionArray, - const QTransform &worldTransform, - bool allItems, - const QRegion &exposedRegion) const -{ - // Two unit vectors. - QLineF v1(0, 0, 1, 0); - QLineF v2(0, 0, 0, 1); - QTransform itemToViewportTransform; - QRectF brect; - QTransform reverseMap; - - for (int i = 0; i < itemList.size(); ++i) { - QGraphicsItem *item = itemArray[i] = itemList[i]; - - QStyleOptionGraphicsItem &option = styleOptionArray[i]; - brect = item->boundingRect(); - option.state = QStyle::State_None; - option.rect = brect.toRect(); - option.exposedRect = QRectF(); - if (item->d_ptr->selected) - option.state |= QStyle::State_Selected; - if (item->d_ptr->enabled) - option.state |= QStyle::State_Enabled; - if (item->hasFocus()) - option.state |= QStyle::State_HasFocus; - if (scene->d_func()->hoverItems.contains(item)) - option.state |= QStyle::State_MouseOver; - if (item == scene->mouseGrabberItem()) - option.state |= QStyle::State_Sunken; - - // Calculate a simple level-of-detail metric. - // ### almost identical code in QGraphicsScene::render() - // and QGraphicsView::render() - consider refactoring - itemToViewportTransform = item->deviceTransform(worldTransform); - - if (itemToViewportTransform.type() <= QTransform::TxTranslate) { - // Translation and rotation only? The LOD is 1. - option.levelOfDetail = 1; - } else { - // LOD is the transformed area of a 1x1 rectangle. - option.levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length()); - } - option.matrix = itemToViewportTransform.toAffine(); //### discards perspective - - if (!allItems) { - // Determine the item's exposed area - reverseMap = itemToViewportTransform.inverted(); - foreach (const QRect &rect, exposedRegion.rects()) { - option.exposedRect |= reverseMap.mapRect(QRectF(rect.adjusted(-1, -1, 1, 1))); - if (option.exposedRect.contains(brect)) - break; - } - option.exposedRect &= brect; - } else { - // The whole item is exposed - option.exposedRect = brect; - } - } + const QPainterPath exposedScenePath(q->mapToScene(qt_regionToPath(adjustedRegion))); + return scene->items(exposedScenePath, Qt::IntersectsItemBoundingRect, + Qt::DescendingOrder, viewTransform); +} + +/*! + \internal + + Enables input methods for the view if and only if the current focus item of + the scene accepts input methods. Call function whenever that condition has + potentially changed. +*/ +void QGraphicsViewPrivate::updateInputMethodSensitivity() +{ + Q_Q(QGraphicsView); + q->setAttribute( + Qt::WA_InputMethodEnabled, + scene && scene->focusItem() + && scene->focusItem()->flags() & QGraphicsItem::ItemAcceptsInputMethod); } /*! @@ -1274,7 +1107,7 @@ void QGraphicsView::setRenderHints(QPainter::RenderHints hints) if (hints == d->renderHints) return; d->renderHints = hints; - viewport()->update(); + d->updateAll(); } /*! @@ -1292,7 +1125,7 @@ void QGraphicsView::setRenderHint(QPainter::RenderHint hint, bool enabled) else d->renderHints &= ~hint; if (oldHints != d->renderHints) - viewport()->update(); + d->updateAll(); } /*! @@ -1579,7 +1412,7 @@ void QGraphicsView::resetCachedContent() if (d->cacheMode & CacheBackground) { // Background caching is enabled. d->mustResizeBackgroundPixmap = true; - viewport()->update(); + d->updateAll(); } else if (d->mustResizeBackgroundPixmap) { // Background caching is disabled. // Cleanup, free some resources. @@ -1668,7 +1501,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) return; // Always update the viewport when the scene changes. - viewport()->update(); + d->updateAll(); // Remove the previously assigned scene. if (d->scene) { @@ -1676,7 +1509,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) this, SLOT(updateScene(QList<QRectF>))); disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect(QRectF))); - d->scene->d_func()->views.removeAll(this); + d->scene->d_func()->removeView(this); d->connectedToScene = false; } @@ -1685,13 +1518,25 @@ void QGraphicsView::setScene(QGraphicsScene *scene) connect(d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect(QRectF))); d->updateSceneSlotReimplementedChecked = false; - d->scene->d_func()->views << this; + d->scene->d_func()->addView(this); d->recalculateContentSize(); d->lastCenterPoint = sceneRect().center(); d->keepLastCenterPoint = true; + // We are only interested in mouse tracking if items accept + // hover events or use non-default cursors. + if (!d->scene->d_func()->allItemsIgnoreHoverEvents + || !d->scene->d_func()->allItemsUseDefaultCursor) { + d->viewport->setMouseTracking(true); + } + + // enable touch events if any items is interested in them + if (!d->scene->d_func()->allItemsIgnoreTouchEvents) + d->viewport->setAttribute(Qt::WA_AcceptTouchEvents); } else { d->recalculateContentSize(); } + + d->updateInputMethodSensitivity(); } /*! @@ -1738,7 +1583,7 @@ void QGraphicsView::setSceneRect(const QRectF &rect) Returns the current transformation matrix for the view. If no current transformation is set, the identity matrix is returned. - \sa setMatrix(), rotate(), scale(), shear(), translate() + \sa setMatrix(), transform(), rotate(), scale(), shear(), translate() */ QMatrix QGraphicsView::matrix() const { @@ -1770,7 +1615,7 @@ QMatrix QGraphicsView::matrix() const a view coordinate to a floating point scene coordinate, or mapFromScene() to map from floating point scene coordinates to view coordinates. - \sa matrix(), rotate(), scale(), shear(), translate() + \sa matrix(), setTransform(), rotate(), scale(), shear(), translate() */ void QGraphicsView::setMatrix(const QMatrix &matrix, bool combine) { @@ -1779,6 +1624,8 @@ void QGraphicsView::setMatrix(const QMatrix &matrix, bool combine) /*! Resets the view transformation matrix to the identity matrix. + + \sa resetTransform() */ void QGraphicsView::resetMatrix() { @@ -1788,7 +1635,7 @@ void QGraphicsView::resetMatrix() /*! Rotates the current view transformation \a angle degrees clockwise. - \sa setMatrix(), matrix(), scale(), shear(), translate() + \sa setTransform(), transform(), scale(), shear(), translate() */ void QGraphicsView::rotate(qreal angle) { @@ -1801,7 +1648,7 @@ void QGraphicsView::rotate(qreal angle) /*! Scales the current view transformation by (\a sx, \a sy). - \sa setMatrix(), matrix(), rotate(), shear(), translate() + \sa setTransform(), transform(), rotate(), shear(), translate() */ void QGraphicsView::scale(qreal sx, qreal sy) { @@ -1814,7 +1661,7 @@ void QGraphicsView::scale(qreal sx, qreal sy) /*! Shears the current view transformation by (\a sh, \a sv). - \sa setMatrix(), matrix(), rotate(), scale(), translate() + \sa setTransform(), transform(), rotate(), scale(), translate() */ void QGraphicsView::shear(qreal sh, qreal sv) { @@ -1827,7 +1674,7 @@ void QGraphicsView::shear(qreal sh, qreal sv) /*! Translates the current view transformation by (\a dx, \a dy). - \sa setMatrix(), matrix(), rotate(), shear() + \sa setTransform(), transform(), rotate(), shear() */ void QGraphicsView::translate(qreal dx, qreal dy) { @@ -1985,7 +1832,7 @@ void QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin, int ym If \a rect is empty, or if the viewport is too small, this function will do nothing. - \sa setMatrix(), ensureVisible(), centerOn() + \sa setTransform(), ensureVisible(), centerOn() */ void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode) { @@ -2050,7 +1897,12 @@ void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRati void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode) { QPainterPath path = item->isClipped() ? item->clipPath() : item->shape(); - fitInView(item->sceneTransform().map(path).boundingRect(), aspectRatioMode); + if (item->d_ptr->hasTranslateOnlySceneTransform()) { + path.translate(item->d_ptr->sceneTransform.dx(), item->d_ptr->sceneTransform.dy()); + fitInView(path.boundingRect(), aspectRatioMode); + } else { + fitInView(item->d_ptr->sceneTransform.map(path).boundingRect(), aspectRatioMode); + } } /*! @@ -2076,6 +1928,8 @@ void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode asp void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source, Qt::AspectRatioMode aspectRatioMode) { + // ### Switch to using the recursive rendering algorithm instead. + Q_D(QGraphicsView); if (!d->scene || !(painter && painter->isActive())) return; @@ -2122,48 +1976,17 @@ void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect itemList.clear(); // Setup painter matrix. - QTransform moveMatrix; - moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll()); + QTransform moveMatrix = QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll()); QTransform painterMatrix = d->matrix * moveMatrix; painterMatrix *= QTransform() .translate(targetRect.left(), targetRect.top()) .scale(xratio, yratio) .translate(-sourceRect.left(), -sourceRect.top()); - // Two unit vectors. - QLineF v1(0, 0, 1, 0); - QLineF v2(0, 0, 0, 1); - // Generate the style options QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems); - QStyleOptionGraphicsItem* option = styleOptionArray; - for (int i = 0; i < numItems; ++i, ++option) { - QGraphicsItem *item = itemArray[i]; - - option->state = QStyle::State_None; - option->rect = item->boundingRect().toRect(); - if (item->isSelected()) - option->state |= QStyle::State_Selected; - if (item->isEnabled()) - option->state |= QStyle::State_Enabled; - if (item->hasFocus()) - option->state |= QStyle::State_HasFocus; - if (d->scene->d_func()->hoverItems.contains(item)) - option->state |= QStyle::State_MouseOver; - if (item == d->scene->mouseGrabberItem()) - option->state |= QStyle::State_Sunken; - - // Calculate a simple level-of-detail metric. - // ### almost identical code in QGraphicsScene::render() - // and QGraphicsView::paintEvent() - consider refactoring - QTransform itemToViewportTransform = item->deviceTransform(painterMatrix); - - option->levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length()); - option->matrix = itemToViewportTransform.toAffine(); - - option->exposedRect = item->boundingRect(); - option->exposedRect &= itemToViewportTransform.inverted().mapRect(targetRect); - } + for (int i = 0; i < numItems; ++i) + itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterMatrix, targetRect.toRect()); painter->save(); @@ -2203,69 +2026,6 @@ QList<QGraphicsItem *> QGraphicsView::items() const } /*! - Returns all items in the area \a path, which is in viewport coordinates, - also taking untransformable items into consideration. This function is - considerably slower than just checking the scene directly. There is - certainly room for improvement. -*/ -QList<QGraphicsItem *> QGraphicsViewPrivate::itemsInArea(const QPainterPath &path, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - Q_Q(const QGraphicsView); - - // Determine the size of the largest untransformable subtree of children - // mapped to scene coordinates. - QRectF untr = scene->d_func()->largestUntransformableItem; - QRectF ltri = matrix.inverted().mapRect(untr); - ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height()); - - QRectF rect = path.controlPointRect(); - - // Find all possible items in the relevant area. - // ### Improve this algorithm; it might be searching a too large area. - QRectF adjustedRect = q->mapToScene(rect.adjusted(-1, -1, 1, 1).toRect()).boundingRect(); - adjustedRect.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height()); - - // First build a (potentially large) list of all items in the vicinity - // that might be untransformable. - QList<QGraphicsItem *> allCandidates = scene->d_func()->estimateItemsInRect(adjustedRect); - - // Then find the minimal list of items that are inside \a path, and - // convert it to a set. - QList<QGraphicsItem *> regularCandidates = scene->items(q->mapToScene(path), mode); - QSet<QGraphicsItem *> candSet = QSet<QGraphicsItem *>::fromList(regularCandidates); - - QTransform viewMatrix = q->viewportTransform(); - - QList<QGraphicsItem *> result; - - // Run through all candidates and keep all items that are in candSet, or - // are untransformable and collide with \a path. ### We can improve this - // algorithm. - QList<QGraphicsItem *>::Iterator it = allCandidates.begin(); - while (it != allCandidates.end()) { - QGraphicsItem *item = *it; - if (item->d_ptr->itemIsUntransformable()) { - // Check if this untransformable item collides with the - // original selection rect. - QTransform itemTransform = item->deviceTransform(viewMatrix); - if (QGraphicsScenePrivate::itemCollidesWithPath(item, itemTransform.inverted().map(path), mode)) - result << item; - } else { - if (candSet.contains(item)) - result << item; - } - ++it; - } - - // ### Insertion sort would be faster. - if (order != Qt::SortOrder(-1)) - QGraphicsScenePrivate::sortItems(&result, order, scene->d_func()->sortCacheEnabled); - return result; -} - -/*! Returns a list of all the items at the position \a pos in the view. The items are listed in descending Z order (i.e., the first item in the list is the top-most item, and the last item is the bottom-most item). \a pos @@ -2284,17 +2044,22 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) { - if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) { - QTransform xinv = viewportTransform().inverted(); - return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1))); - } - return d->scene->items(mapToScene(pos.x(), pos.y(), 2, 2)); + // ### Unify these two, and use the items(QPointF) version in + // QGraphicsScene instead. The scene items function could use the viewport + // transform to map the point to a rect/polygon. + if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) { + // Use the rect version + QTransform xinv = viewportTransform().inverted(); + return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)), + Qt::IntersectsItemShape, + Qt::AscendingOrder, + viewportTransform()); } - - QPainterPath path; - path.addRect(QRectF(pos.x(), pos.y(), 1, 1)); - return d->itemsInArea(path); + // Use the polygon version + return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1), + Qt::IntersectsItemShape, + Qt::AscendingOrder, + viewportTransform()); } /*! @@ -2321,12 +2086,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelection Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) - return d->scene->items(mapToScene(rect), mode); - - QPainterPath path; - path.addRect(rect); - return d->itemsInArea(path); + return d->scene->items(mapToScene(rect), mode, Qt::AscendingOrder, viewportTransform()); } /*! @@ -2354,13 +2114,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSel Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) - return d->scene->items(mapToScene(polygon), mode); - - QPainterPath path; - path.addPolygon(polygon); - path.closeSubpath(); - return d->itemsInArea(path); + return d->scene->items(mapToScene(polygon), mode, Qt::AscendingOrder, viewportTransform()); } /*! @@ -2380,9 +2134,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSe Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) - return d->scene->items(mapToScene(path), mode); - return d->itemsInArea(path); + return d->scene->items(mapToScene(path), mode, Qt::AscendingOrder, viewportTransform()); } /*! @@ -2451,10 +2203,11 @@ QPolygonF QGraphicsView::mapToScene(const QRect &rect) const return QPolygonF(); QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll()); - QPointF tl = scrollOffset + rect.topLeft(); - QPointF tr = scrollOffset + rect.topRight(); - QPointF br = scrollOffset + rect.bottomRight(); - QPointF bl = scrollOffset + rect.bottomLeft(); + QRect r = rect.adjusted(0, 0, 1, 1); + QPointF tl = scrollOffset + r.topLeft(); + QPointF tr = scrollOffset + r.topRight(); + QPointF br = scrollOffset + r.bottomRight(); + QPointF bl = scrollOffset + r.bottomLeft(); QPolygonF poly; poly.resize(4); @@ -2503,9 +2256,9 @@ QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const { Q_D(const QGraphicsView); - QTransform moveMatrix; - moveMatrix.translate(d->horizontalScroll(), d->verticalScroll()); - return (moveMatrix * d->matrix.inverted()).map(path); + QTransform matrix = QTransform::fromTranslate(d->horizontalScroll(), d->verticalScroll()); + matrix *= d->matrix.inverted(); + return matrix.map(path); } /*! @@ -2599,9 +2352,9 @@ QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const QPainterPath QGraphicsView::mapFromScene(const QPainterPath &path) const { Q_D(const QGraphicsView); - QTransform moveMatrix; - moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll()); - return (d->matrix * moveMatrix).map(path); + QTransform matrix = d->matrix; + matrix *= QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll()); + return matrix.map(path); } /*! @@ -2647,7 +2400,7 @@ void QGraphicsView::setBackgroundBrush(const QBrush &brush) { Q_D(QGraphicsView); d->backgroundBrush = brush; - viewport()->update(); + d->updateAll(); if (d->cacheMode & CacheBackground) { // Invalidate the background pixmap @@ -2677,7 +2430,7 @@ void QGraphicsView::setForegroundBrush(const QBrush &brush) { Q_D(QGraphicsView); d->foregroundBrush = brush; - viewport()->update(); + d->updateAll(); } /*! @@ -2698,9 +2451,11 @@ void QGraphicsView::updateScene(const QList<QRectF> &rects) // Extract and reset dirty scene rect info. QVector<QRect> dirtyViewportRects; - for (int i = 0; i < d->dirtyRegions.size(); ++i) - dirtyViewportRects += d->dirtyRegions.at(i).rects(); - d->dirtyRegions.clear(); + const QVector<QRect> &dirtyRects = d->dirtyRegion.rects(); + for (int i = 0; i < dirtyRects.size(); ++i) + dirtyViewportRects += dirtyRects.at(i); + d->dirtyRegion = QRegion(); + d->dirtyBoundingRect = QRect(); bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate; bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) @@ -2784,9 +2539,7 @@ void QGraphicsView::setupViewport(QWidget *widget) const bool isGLWidget = widget->inherits("QGLWidget"); - d->accelerateScrolling = !(isGLWidget - || widget->testAttribute(Qt::WA_MSWindowsUseDirect3D) - || qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)); + d->accelerateScrolling = !(isGLWidget); widget->setFocusPolicy(Qt::StrongFocus); @@ -2795,7 +2548,17 @@ void QGraphicsView::setupViewport(QWidget *widget) widget->setAutoFillBackground(true); } - widget->setMouseTracking(true); + // We are only interested in mouse tracking if items + // accept hover events or use non-default cursors. + if (d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents + || !d->scene->d_func()->allItemsUseDefaultCursor)) { + widget->setMouseTracking(true); + } + + // enable touch events if any items is interested in them + if (d->scene && !d->scene->d_func()->allItemsIgnoreTouchEvents) + widget->setAttribute(Qt::WA_AcceptTouchEvents); + widget->setAcceptDrops(acceptDrops()); } @@ -2894,6 +2657,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) case QEvent::Paint: // Reset full update d->fullUpdatePending = false; + d->dirtyScrollOffset = QPoint(); if (d->scene) { // Check if this view reimplements the updateScene slot; if it // does, we can't do direct update delivery and have to fall back @@ -2912,6 +2676,23 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->updateAll = false; } break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + if (!isEnabled()) + return false; + + if (d->scene && d->sceneInteractionAllowed) { + // Convert and deliver the touch event to the scene. + QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event); + touchEvent->setWidget(viewport()); + QGraphicsViewPrivate::translateTouchEvent(d, touchEvent); + (void) QApplication::sendEvent(d->scene, touchEvent); + } + + return true; + } default: break; } @@ -3085,6 +2866,7 @@ void QGraphicsView::dragMoveEvent(QDragMoveEvent *event) void QGraphicsView::focusInEvent(QFocusEvent *event) { Q_D(QGraphicsView); + d->updateInputMethodSensitivity(); QAbstractScrollArea::focusInEvent(event); if (d->scene) QApplication::sendEvent(d->scene, event); @@ -3170,7 +2952,10 @@ void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) mouseEvent.setAccepted(false); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); - QApplication::sendEvent(d->scene, &mouseEvent); + if (event->spontaneous()) + qt_sendSpontaneousEvent(d->scene, &mouseEvent); + else + QApplication::sendEvent(d->scene, &mouseEvent); } /*! @@ -3209,7 +2994,10 @@ void QGraphicsView::mousePressEvent(QMouseEvent *event) mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); mouseEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &mouseEvent); + if (event->spontaneous()) + qt_sendSpontaneousEvent(d->scene, &mouseEvent); + else + QApplication::sendEvent(d->scene, &mouseEvent); // Update the original mouse event accepted state. bool isAccepted = mouseEvent.isAccepted(); @@ -3270,7 +3058,7 @@ void QGraphicsView::mouseMoveEvent(QMouseEvent *event) if (d->viewportUpdateMode != FullViewportUpdate) viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); else - viewport()->update(); + d->updateAll(); } // Stop rubber banding if the user has let go of all buttons (even @@ -3292,14 +3080,15 @@ void QGraphicsView::mouseMoveEvent(QMouseEvent *event) if (d->viewportUpdateMode != FullViewportUpdate) viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); else - viewport()->update(); + d->updateAll(); } // Set the new selection area QPainterPath selectionArea; selectionArea.addPolygon(mapToScene(d->rubberBandRect)); selectionArea.closeSubpath(); if (d->scene) - d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode); + d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode, + viewportTransform()); return; } } else @@ -3335,7 +3124,7 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) if (d->viewportUpdateMode != FullViewportUpdate) viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); else - viewport()->update(); + d->updateAll(); } d->rubberBanding = false; d->rubberBandRect = QRect(); @@ -3379,7 +3168,10 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); mouseEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &mouseEvent); + if (event->spontaneous()) + qt_sendSpontaneousEvent(d->scene, &mouseEvent); + else + QApplication::sendEvent(d->scene, &mouseEvent); // Update the last mouse event selected state. d->lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -3437,16 +3229,11 @@ void QGraphicsView::paintEvent(QPaintEvent *event) d->scene->d_func()->painterStateProtection = !(d->optimizationFlags & DontSavePainterState); // Determine the exposed region - QRegion exposedRegion = event->region(); - if (!d->accelerateScrolling) - exposedRegion = viewport()->rect(); - else if (d->viewportUpdateMode == BoundingRectViewportUpdate) - exposedRegion = event->rect(); - QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect().adjusted(0, 0, 1, 1)).boundingRect(); + d->exposedRegion = event->region(); + QRectF exposedSceneRect = mapToScene(d->exposedRegion.boundingRect()).boundingRect(); // Set up the painter QPainter painter(viewport()); - QTransform original = painter.worldTransform(); #ifndef QT_NO_RUBBERBAND if (d->rubberBanding && !d->rubberBandRect.isEmpty()) painter.save(); @@ -3456,23 +3243,12 @@ void QGraphicsView::paintEvent(QPaintEvent *event) painter.setRenderHints(d->renderHints, true); // Set up viewport transform - const QTransform viewTransform = viewportTransform(); - painter.setTransform(viewTransform, true); - -#ifdef QGRAPHICSVIEW_DEBUG - QTime stopWatch; - stopWatch.start(); - qDebug() << "QGraphicsView::paintEvent(" << exposedRegion << ")"; -#endif - - // Find all exposed items - bool allItems = false; - QList<QGraphicsItem *> itemList = d->findItems(exposedRegion, &allItems); - -#ifdef QGRAPHICSVIEW_DEBUG - int exposedTime = stopWatch.elapsed(); -#endif + const bool viewTransformed = isTransformed(); + if (viewTransformed) + painter.setWorldTransform(viewportTransform()); + const QTransform viewTransform = painter.worldTransform(); + // Draw background if ((d->cacheMode & CacheBackground) #ifdef Q_WS_X11 && X11->use_xrender @@ -3495,16 +3271,21 @@ void QGraphicsView::paintEvent(QPaintEvent *event) if (!d->backgroundPixmapExposed.isEmpty()) { QPainter backgroundPainter(&d->backgroundPixmap); backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip); - backgroundPainter.setTransform(viewportTransform()); + if (viewTransformed) + backgroundPainter.setTransform(viewTransform); + backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source); drawBackground(&backgroundPainter, exposedSceneRect); d->backgroundPixmapExposed = QRegion(); } // Blit the background from the background pixmap - QTransform oldMatrix = painter.worldTransform(); - painter.setWorldTransform(original); - painter.drawPixmap(QPoint(), d->backgroundPixmap); - painter.setWorldTransform(oldMatrix); + if (viewTransformed) { + painter.setWorldTransform(QTransform()); + painter.drawPixmap(QPoint(), d->backgroundPixmap); + painter.setWorldTransform(viewTransform); + } else { + painter.drawPixmap(QPoint(), d->backgroundPixmap); + } } else { if (!(d->optimizationFlags & DontSavePainterState)) painter.save(); @@ -3513,34 +3294,32 @@ void QGraphicsView::paintEvent(QPaintEvent *event) painter.restore(); } -#ifdef QGRAPHICSVIEW_DEBUG - int backgroundTime = stopWatch.elapsed() - exposedTime; -#endif - - // Generate the style options - QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()]; - QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(itemList.size()); - - d->generateStyleOptions(itemList, itemArray, styleOptionArray, viewTransform, - allItems, exposedRegion); - // Items - drawItems(&painter, itemList.size(), itemArray, styleOptionArray); - -#ifdef QGRAPHICSVIEW_DEBUG - int itemsTime = stopWatch.elapsed() - exposedTime - backgroundTime; -#endif + if (!(d->optimizationFlags & IndirectPainting)) { + d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : 0, + &d->exposedRegion, viewport()); + } else { + // Find all exposed items + bool allItems = false; + QList<QGraphicsItem *> itemList = d->findItems(d->exposedRegion, &allItems, viewTransform); + if (!itemList.isEmpty()) { + // Generate the style options. + const int numItems = itemList.size(); + QGraphicsItem **itemArray = &itemList[0]; // Relies on QList internals, but is perfectly valid. + QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems); + for (int i = 0; i < numItems; ++i) { + itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], viewTransform, + d->exposedRegion, allItems); + } + // Draw the items. + drawItems(&painter, numItems, itemArray, styleOptionArray); + d->freeStyleOptionsArray(styleOptionArray); + } + } // Foreground drawForeground(&painter, exposedSceneRect); - delete [] itemArray; - d->freeStyleOptionsArray(styleOptionArray); - -#ifdef QGRAPHICSVIEW_DEBUG - int foregroundTime = stopWatch.elapsed() - exposedTime - backgroundTime - itemsTime; -#endif - #ifndef QT_NO_RUBBERBAND // Rubberband if (d->rubberBanding && !d->rubberBandRect.isEmpty()) { @@ -3562,17 +3341,6 @@ void QGraphicsView::paintEvent(QPaintEvent *event) painter.end(); -#ifdef QGRAPHICSVIEW_DEBUG - qDebug() << "\tItem discovery....... " << exposedTime << "msecs (" << itemList.size() << "items," - << (exposedTime > 0 ? (itemList.size() * 1000.0 / exposedTime) : -1) << "/ sec )"; - qDebug() << "\tDrawing background... " << backgroundTime << "msecs (" << exposedRegion.numRects() << "segments )"; - qDebug() << "\tDrawing items........ " << itemsTime << "msecs (" - << (itemsTime > 0 ? (itemList.size() * 1000.0 / itemsTime) : -1) << "/ sec )"; - qDebug() << "\tDrawing foreground... " << foregroundTime << "msecs (" << exposedRegion.numRects() << "segments )"; - qDebug() << "\tTotal rendering time: " << stopWatch.elapsed() << "msecs (" - << (stopWatch.elapsed() > 0 ? (1000.0 / stopWatch.elapsed()) : -1.0) << "fps )"; -#endif - // Restore painter state protection. d->scene->d_func()->painterStateProtection = true; } @@ -3619,10 +3387,6 @@ void QGraphicsView::scrollContentsBy(int dx, int dy) if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate) { if (d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) { - for (int i = 0; i < d->dirtyRects.size(); ++i) - d->dirtyRects[i].translate(dx, dy); - for (int i = 0; i < d->dirtyRegions.size(); ++i) - d->dirtyRegions[i].translate(dx, dy); if (d->accelerateScrolling) { #ifndef QT_NO_RUBBERBAND // Update new and old rubberband regions @@ -3632,12 +3396,15 @@ void QGraphicsView::scrollContentsBy(int dx, int dy) viewport()->update(rubberBandRegion); } #endif + d->dirtyScrollOffset.rx() += dx; + d->dirtyScrollOffset.ry() += dy; + d->dirtyRegion.translate(dx, dy); viewport()->scroll(dx, dy); } else { - viewport()->update(); + d->updateAll(); } } else { - viewport()->update(); + d->updateAll(); } } @@ -3648,31 +3415,14 @@ void QGraphicsView::scrollContentsBy(int dx, int dy) && X11->use_xrender #endif ) { - // Invalidate the background pixmap - d->backgroundPixmapExposed.translate(dx, 0); - if (dx > 0) { - d->backgroundPixmapExposed += QRect(0, 0, dx, viewport()->height()); - } else if (dx < 0) { - d->backgroundPixmapExposed += QRect(viewport()->width() + dx, 0, - -dx, viewport()->height()); - } - d->backgroundPixmapExposed.translate(0, dy); - if (dy > 0) { - d->backgroundPixmapExposed += QRect(0, 0, viewport()->width(), dy); - } else if (dy < 0) { - d->backgroundPixmapExposed += QRect(0, viewport()->height() + dy, - viewport()->width(), -dy); - } - // Scroll the background pixmap - if (!d->backgroundPixmap.isNull()) { - QPixmap tmp = d->backgroundPixmap.copy(); - QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole()); - if (!bgBrush.isOpaque()) - d->backgroundPixmap.fill(Qt::transparent); - QPainter painter(&d->backgroundPixmap); - painter.drawPixmap(dx, dy, tmp); - } + QRegion exposed; + if (!d->backgroundPixmap.isNull()) + d->backgroundPixmap.scroll(dx, dy, d->backgroundPixmap.rect(), &exposed); + + // Invalidate the background pixmap + d->backgroundPixmapExposed.translate(dx, dy); + d->backgroundPixmapExposed += exposed; } // Always replay on scroll. @@ -3773,8 +3523,10 @@ void QGraphicsView::drawItems(QPainter *painter, int numItems, const QStyleOptionGraphicsItem options[]) { Q_D(QGraphicsView); - if (d->scene) - d->scene->drawItems(painter, numItems, items, options, viewport()); + if (d->scene) { + QWidget *widget = painter->device() == viewport() ? viewport() : 0; + d->scene->drawItems(painter, numItems, items, options, widget); + } } /*! @@ -3797,12 +3549,25 @@ QTransform QGraphicsView::transform() const QTransform QGraphicsView::viewportTransform() const { Q_D(const QGraphicsView); - QTransform moveMatrix; - moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll()); + QTransform moveMatrix = QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll()); return d->identityMatrix ? moveMatrix : d->matrix * moveMatrix; } /*! + \since 4.6 + + Returns true if the view is transformed (i.e., a non-identity transform + has been assigned, or the scrollbars are adjusted). + + \sa setTransform(), horizontalScrollBar(), verticalScrollBar() +*/ +bool QGraphicsView::isTransformed() const +{ + Q_D(const QGraphicsView); + return !d->identityMatrix || d->horizontalScroll() || d->verticalScroll(); +} + +/*! Sets the view's current transformation matrix to \a matrix. If \a combine is true, then \a matrix is combined with the current matrix; @@ -3853,7 +3618,7 @@ void QGraphicsView::setTransform(const QTransform &matrix, bool combine ) d->transforming = false; // Any matrix operation requires a full update. - viewport()->update(); + d->updateAll(); } /*! @@ -3866,6 +3631,39 @@ void QGraphicsView::resetTransform() setTransform(QTransform()); } +QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const +{ + QPointF p = point; + p.rx() += horizontalScroll(); + p.ry() += verticalScroll(); + return identityMatrix ? p : matrix.inverted().map(p); +} + +QRectF QGraphicsViewPrivate::mapToScene(const QRectF &rect) const +{ + QPointF scrollOffset(horizontalScroll(), verticalScroll()); + QPointF tl = scrollOffset + rect.topLeft(); + QPointF tr = scrollOffset + rect.topRight(); + QPointF br = scrollOffset + rect.bottomRight(); + QPointF bl = scrollOffset + rect.bottomLeft(); + + QPolygonF poly; + poly.resize(4); + if (!identityMatrix) { + QTransform x = matrix.inverted(); + poly[0] = x.map(tl); + poly[1] = x.map(tr); + poly[2] = x.map(br); + poly[3] = x.map(bl); + } else { + poly[0] = tl; + poly[1] = tr; + poly[2] = br; + poly[3] = bl; + } + return poly.boundingRect(); +} + QT_END_NAMESPACE #include "moc_qgraphicsview.cpp" diff --git a/src/gui/graphicsview/qgraphicsview.h b/src/gui/graphicsview/qgraphicsview.h index 4426655..1285e45 100644 --- a/src/gui/graphicsview/qgraphicsview.h +++ b/src/gui/graphicsview/qgraphicsview.h @@ -110,9 +110,10 @@ public: }; enum OptimizationFlag { - DontClipPainter = 0x1, + DontClipPainter = 0x1, // obsolete DontSavePainterState = 0x2, - DontAdjustForAntialiasing = 0x4 + DontAdjustForAntialiasing = 0x4, + IndirectPainting = 0x8 }; Q_DECLARE_FLAGS(OptimizationFlags, OptimizationFlag) @@ -169,6 +170,7 @@ public: void resetMatrix(); QTransform transform() const; QTransform viewportTransform() const; + bool isTransformed() const; void setTransform(const QTransform &matrix, bool combine = false); void resetTransform(); void rotate(qreal angle); @@ -273,7 +275,6 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_setViewportCursor(const QCursor &)) Q_PRIVATE_SLOT(d_func(), void _q_unsetViewportCursor()) #endif - Q_PRIVATE_SLOT(d_func(), void _q_updateLaterSlot()) friend class QGraphicsSceneWidget; friend class QGraphicsScene; friend class QGraphicsScenePrivate; diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index e4daebf..62a2b84 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -58,12 +58,14 @@ #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW #include <QtGui/qevent.h> +#include <QtCore/qcoreapplication.h> #include "qgraphicssceneevent.h" +#include <QtGui/qstyleoption.h> #include <private/qabstractscrollarea_p.h> QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate +class Q_AUTOTEST_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate { Q_DECLARE_PUBLIC(QGraphicsView) public: @@ -84,16 +86,13 @@ public: qint64 horizontalScroll() const; qint64 verticalScroll() const; - QList<QGraphicsItem *> itemsInArea(const QPainterPath &path, - Qt::ItemSelectionMode mode = Qt::IntersectsItemShape, - Qt::SortOrder = Qt::AscendingOrder) const; - QPointF mousePressItemPoint; QPointF mousePressScenePoint; QPoint mousePressViewPoint; QPoint mousePressScreenPoint; QPointF lastMouseMoveScenePoint; QPoint lastMouseMoveScreenPoint; + QPoint dirtyScrollOffset; Qt::MouseButton mousePressButton; QTransform matrix; bool identityMatrix; @@ -159,28 +158,38 @@ public: QRect mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const; QRegion mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const; - void itemUpdated(QGraphicsItem *item, const QRectF &rect); bool fullUpdatePending; - QList<QRect> dirtyRects; - QList<QRegion> dirtyRegions; - int dirtyRectCount; + QRegion dirtyRegion; QRect dirtyBoundingRect; - void updateLater(); - bool updatingLater; - void _q_updateLaterSlot(); - void updateAll(); - void updateRect(const QRect &rect); - void updateRegion(const QRegion ®ion); + void processPendingUpdates(); + inline void updateAll() + { + viewport->update(); + fullUpdatePending = true; + dirtyBoundingRect = QRect(); + dirtyRegion = QRegion(); + } + + inline void dispatchPendingUpdateRequests() + { + if (qt_widget_private(viewport)->paintOnScreen()) + QCoreApplication::sendPostedEvents(viewport, QEvent::UpdateRequest); + else + QCoreApplication::sendPostedEvents(viewport->window(), QEvent::UpdateRequest); + } + + bool updateRect(const QRect &rect); + bool updateRegion(const QRegion ®ion); bool updateSceneSlotReimplementedChecked; + QRegion exposedRegion; - QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems) const; + QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems, + const QTransform &viewTransform) const; - void generateStyleOptions(const QList<QGraphicsItem *> &itemList, - QGraphicsItem **itemArray, - QStyleOptionGraphicsItem *styleOptionArray, - const QTransform &worldTransform, - bool allItems, - const QRegion &exposedRegion) const; + QPointF mapToScene(const QPointF &point) const; + QRectF mapToScene(const QRectF &rect) const; + static void translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent); + void updateInputMethodSensitivity(); }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index 7b9b969..3ea80ce 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -170,46 +170,13 @@ QT_BEGIN_NAMESPACE */ /*! - \property QGraphicsWidget::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() -*/ - -/*! - \property QGraphicsWidget::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(), show(), - hide() -*/ - -/*! - \property QGraphicsWidget::opacity - \brief the opacity of the widget -*/ - -/*! - \property QGraphicsWidget::pos - \brief the position of the widget -*/ - -/*! Constructs a QGraphicsWidget instance. The optional \a parent argument is passed to QGraphicsItem's constructor. The optional \a wFlags argument specifies the widget's window flags (e.g., whether the widget should be a window, a tool, a popup, etc). */ QGraphicsWidget::QGraphicsWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) - : QGraphicsItem(*new QGraphicsWidgetPrivate, 0, 0), QGraphicsLayoutItem(0, false) + : QGraphicsObject(*new QGraphicsWidgetPrivate, 0, 0), QGraphicsLayoutItem(0, false) { Q_D(QGraphicsWidget); d->init(parent, wFlags); @@ -221,7 +188,7 @@ QGraphicsWidget::QGraphicsWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) Constructs a new QGraphicsWidget, using \a dd as parent. */ QGraphicsWidget::QGraphicsWidget(QGraphicsWidgetPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene, Qt::WindowFlags wFlags) - : QGraphicsItem(dd, 0, scene), QGraphicsLayoutItem(0, false) + : QGraphicsObject(dd, 0, scene), QGraphicsLayoutItem(0, false) { Q_D(QGraphicsWidget); d->init(parent, wFlags); @@ -459,17 +426,19 @@ void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qre { Q_D(QGraphicsWidget); - if (left == d->leftMargin - && top == d->topMargin - && right == d->rightMargin - && bottom == d->bottomMargin) { + if (!d->margins && left == 0 && top == 0 && right == 0 && bottom == 0) + return; + d->ensureMargins(); + if (left == d->margins[d->Left] + && top == d->margins[d->Top] + && right == d->margins[d->Right] + && bottom == d->margins[d->Bottom]) return; - } - d->leftMargin = left; - d->topMargin = top; - d->rightMargin = right; - d->bottomMargin = bottom; + d->margins[d->Left] = left; + d->margins[d->Top] = top; + d->margins[d->Right] = right; + d->margins[d->Bottom] = bottom; if (QGraphicsLayout *l = d->layout) l->invalidate(); @@ -490,14 +459,16 @@ void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qre void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const { Q_D(const QGraphicsWidget); + if (left || top || right || bottom) + d->ensureMargins(); if (left) - *left = d->leftMargin; + *left = d->margins[d->Left]; if (top) - *top = d->topMargin; + *top = d->margins[d->Top]; if (right) - *right = d->rightMargin; + *right = d->margins[d->Right]; if (bottom) - *bottom = d->bottomMargin; + *bottom = d->margins[d->Bottom]; } /*! @@ -513,16 +484,23 @@ void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom) { Q_D(QGraphicsWidget); - bool unchanged = left == d->leftWindowFrameMargin && top == d->topWindowFrameMargin - && right == d->rightWindowFrameMargin && bottom == d->bottomWindowFrameMargin; + + if (!d->windowFrameMargins && left == 0 && top == 0 && right == 0 && bottom == 0) + return; + d->ensureWindowFrameMargins(); + bool unchanged = + d->windowFrameMargins[d->Left] == left + && d->windowFrameMargins[d->Top] == top + && d->windowFrameMargins[d->Right] == right + && d->windowFrameMargins[d->Bottom] == bottom; if (d->setWindowFrameMargins && unchanged) return; if (!unchanged) prepareGeometryChange(); - d->leftWindowFrameMargin = left; - d->topWindowFrameMargin = top; - d->rightWindowFrameMargin = right; - d->bottomWindowFrameMargin = bottom; + d->windowFrameMargins[d->Left] = left; + d->windowFrameMargins[d->Top] = top; + d->windowFrameMargins[d->Right] = right; + d->windowFrameMargins[d->Bottom] = bottom; d->setWindowFrameMargins = true; } @@ -536,14 +514,16 @@ void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, void QGraphicsWidget::getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const { Q_D(const QGraphicsWidget); + if (left || top || right || bottom) + d->ensureWindowFrameMargins(); if (left) - *left = d->leftWindowFrameMargin; + *left = d->windowFrameMargins[d->Left]; if (top) - *top = d->topWindowFrameMargin; + *top = d->windowFrameMargins[d->Top]; if (right) - *right = d->rightWindowFrameMargin; + *right = d->windowFrameMargins[d->Right]; if (bottom) - *bottom = d->bottomWindowFrameMargin; + *bottom = d->windowFrameMargins[d->Bottom]; } /*! @@ -577,8 +557,10 @@ void QGraphicsWidget::unsetWindowFrameMargins() QRectF QGraphicsWidget::windowFrameGeometry() const { Q_D(const QGraphicsWidget); - return geometry().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin, - d->rightWindowFrameMargin, d->bottomWindowFrameMargin); + return d->windowFrameMargins + ? geometry().adjusted(-d->windowFrameMargins[d->Left], -d->windowFrameMargins[d->Top], + d->windowFrameMargins[d->Right], d->windowFrameMargins[d->Bottom]) + : geometry(); } /*! @@ -589,8 +571,10 @@ QRectF QGraphicsWidget::windowFrameGeometry() const QRectF QGraphicsWidget::windowFrameRect() const { Q_D(const QGraphicsWidget); - return rect().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin, - d->rightWindowFrameMargin, d->bottomWindowFrameMargin); + return d->windowFrameMargins + ? rect().adjusted(-d->windowFrameMargins[d->Left], -d->windowFrameMargins[d->Top], + d->windowFrameMargins[d->Right], d->windowFrameMargins[d->Bottom]) + : rect(); } /*! @@ -699,7 +683,10 @@ QSizeF QGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) c QSizeF sh; if (d->layout) { sh = d->layout->effectiveSizeHint(which, constraint); - sh += QSizeF(d->leftMargin + d->rightMargin, d->topMargin + d->bottomMargin); + if (d->margins) { + sh += QSizeF(d->margins[d->Left] + d->margins[d->Right], + d->margins[d->Top] + d->margins[d->Bottom]); + } } else { switch (which) { case Qt::MinimumSize: @@ -984,12 +971,18 @@ void QGraphicsWidget::updateGeometry() deliver events related to state changes in the item. Because of this, it is very important that subclasses call the base implementation. - For example, QGraphicsWidget uses ItemVisibleChange to deliver \l Show and - \l Hide events, ItemPositionHasChanged to deliver \l Move events, and - ItemParentChange both to deliver \l ParentChange events, and for managing - the focus chain. + \a change specifies the type of change, and \a value is the new value. - \sa propertyChange() + For example, QGraphicsWidget uses ItemVisibleChange to deliver + \l{QEvent::Show} {Show} and \l{QEvent::Hide}{Hide} events, + ItemPositionHasChanged to deliver \l{QEvent::Move}{Move} events, + and ItemParentChange both to deliver \l{QEvent::ParentChange} + {ParentChange} events, and for managing the focus chain. + + QGraphicsWidget enables the ItemSendsGeometryChanges flag by default in + order to track position changes. + + \sa QGraphicsItem::itemChange() */ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value) { @@ -1039,10 +1032,6 @@ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant & break; } case ItemParentHasChanged: { - // reset window type on parent change in order to automagically remove decorations etc. - Qt::WindowFlags wflags = d->windowFlags & ~Qt::WindowType_Mask; - d->adjustWindowFlags(&wflags); - setWindowFlags(wflags); // Deliver ParentChange. QEvent event(QEvent::ParentChange); QApplication::sendEvent(this, &event); @@ -1131,7 +1120,8 @@ bool QGraphicsWidget::windowFrameEvent(QEvent *event) d->windowFrameMousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); break; case QEvent::GraphicsSceneMouseMove: - if (d->grabbedSection != Qt::NoSection) { + d->ensureWindowData(); + if (d->windowData->grabbedSection != Qt::NoSection) { d->windowFrameMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); event->accept(); } @@ -1186,7 +1176,8 @@ Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) const qreal cornerMargin = 20; //### Not sure of this one, it should be the same value for all edges. - const qreal windowFrameWidth = d->leftWindowFrameMargin; + const qreal windowFrameWidth = d->windowFrameMargins + ? d->windowFrameMargins[d->Left] : 0; Qt::WindowFrameSection s = Qt::NoSection; if (x <= left + cornerMargin) { @@ -1212,7 +1203,8 @@ Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) } if (s == Qt::NoSection) { QRectF r1 = r; - r1.setHeight(d->topWindowFrameMargin); + r1.setHeight(d->windowFrameMargins + ? d->windowFrameMargins[d->Top] : 0); if (r1.contains(pos)) s = Qt::TitleBarArea; } @@ -1222,7 +1214,8 @@ Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) /*! \reimp - QGraphicsWidget handles the following events: + Handles the \a event. QGraphicsWidget handles the following + events: \table \o Event \o Usage \row \o Polish @@ -1320,7 +1313,8 @@ bool QGraphicsWidget::event(QEvent *event) case QEvent::GraphicsSceneMouseMove: case QEvent::GraphicsSceneMouseRelease: case QEvent::GraphicsSceneMouseDoubleClick: - if (d->hasDecoration() && d->grabbedSection != Qt::NoSection) + d->ensureWindowData(); + if (d->hasDecoration() && d->windowData->grabbedSection != Qt::NoSection) return windowFrameEvent(event); break; case QEvent::GraphicsSceneHoverEnter: @@ -1623,6 +1617,7 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) return; bool wasPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup; + d->adjustWindowFlags(&wFlags); d->windowFlags = wFlags; if (!d->setWindowFrameMargins) unsetWindowFrameMargins(); @@ -1635,6 +1630,11 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) else d->scene->d_func()->addPopup(this); } + + if (d->scene && d->scene->d_func()->allItemsIgnoreHoverEvents && d->hasDecoration()) { + d->scene->d_func()->allItemsIgnoreHoverEvents = false; + d->scene->d_func()->enableMouseTrackingOnViews(); + } } /*! @@ -1662,17 +1662,19 @@ bool QGraphicsWidget::isActiveWindow() const This property is only used for windows. - By default, if no title has been set, this property contains an empty string. + By default, if no title has been set, this property contains an + empty string. */ void QGraphicsWidget::setWindowTitle(const QString &title) { Q_D(QGraphicsWidget); - d->windowTitle = title; + d->ensureWindowData(); + d->windowData->windowTitle = title; } QString QGraphicsWidget::windowTitle() const { Q_D(const QGraphicsWidget); - return d->windowTitle; + return d->windowData ? d->windowData->windowTitle : QString(); } /*! @@ -1714,17 +1716,18 @@ void QGraphicsWidget::setFocusPolicy(Qt::FocusPolicy policy) /*! If this widget, a child or descendant of this widget currently has input focus, this function will return a pointer to that widget. If - no descendant has input focus, 0 is returned. + no descendant widget has input focus, 0 is returned. - \sa QWidget::focusWidget() + \sa QGraphicsItem::focusItem(), QWidget::focusWidget() */ QGraphicsWidget *QGraphicsWidget::focusWidget() const { Q_D(const QGraphicsWidget); - return d->focusChild; + if (d->subFocusItem && d->subFocusItem->d_ptr->isWidget) + return static_cast<QGraphicsWidget *>(d->subFocusItem); + return 0; } - #ifndef QT_NO_SHORTCUT /*! \since 4.5 @@ -2110,11 +2113,12 @@ void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGrap QStyleOptionTitleBar bar; bar.QStyleOption::operator=(*option); d->initStyleOptionTitleBar(&bar); // this clear flags in bar.state - if (d->buttonMouseOver) + d->ensureWindowData(); + if (d->windowData->buttonMouseOver) bar.state |= QStyle::State_MouseOver; else bar.state &= ~QStyle::State_MouseOver; - if (d->buttonSunken) + if (d->windowData->buttonSunken) bar.state |= QStyle::State_Sunken; else bar.state &= ~QStyle::State_Sunken; @@ -2249,6 +2253,7 @@ bool QGraphicsWidget::close() #ifdef Q_NO_USING_KEYWORD /*! \fn const QObjectList &QGraphicsWidget::children() const + \internal This function returns the same value as QObject::children(). It's provided to differentiate between the obsolete member @@ -2269,7 +2274,7 @@ void QGraphicsWidget::dumpFocusChain() qWarning("Found a focus chain that is not circular, (next == 0)"); break; } - qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? "1" : "0") << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0); + qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? '1' : '0') << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0); if (visited.contains(next)) { qWarning("Already visited this node. However, I expected to dump until I found myself."); break; diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index 5edca7f..d03a637 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -66,7 +66,7 @@ class QStyleOption; class QGraphicsWidgetPrivate; -class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, public QGraphicsLayoutItem +class Q_GUI_EXPORT QGraphicsWidget : public QGraphicsObject, public QGraphicsLayoutItem { Q_OBJECT Q_PROPERTY(QPalette palette READ palette WRITE setPalette) @@ -74,14 +74,9 @@ class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, publi Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection RESET unsetLayoutDirection) Q_PROPERTY(QSizeF size READ size WRITE resize) Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) - Q_PROPERTY(bool visible READ isVisible WRITE setVisible) Q_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags) Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle) - Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) - Q_PROPERTY(QPointF pos READ pos WRITE setPos) Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) - public: QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); ~QGraphicsWidget(); diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp index 3b692c4..8eac063 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.cpp +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -66,17 +66,20 @@ void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFl isWidget = 1; // QGraphicsItem::isWidget() returns true. focusNext = focusPrev = q; focusPolicy = Qt::NoFocus; + + adjustWindowFlags(&wFlags); + windowFlags = wFlags; + q->setParentItem(parentItem); q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType)); q->setGraphicsItem(q); resolveLayoutDirection(); - - if (!parentItem) - adjustWindowFlags(&wFlags); - windowFlags = wFlags; q->unsetWindowFrameMargins(); + q->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); + q->setFlag(QGraphicsItem::ItemSendsGeometryChanges); } + qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const { Q_Q(const QGraphicsWidget); @@ -89,56 +92,57 @@ qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options return (qreal)height; } -void QGraphicsWidgetPrivate::getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +/*! + \internal +*/ +QGraphicsWidgetPrivate::~QGraphicsWidgetPrivate() { - if (left) - *left = leftLayoutItemMargin; - if (top) - *top = topLayoutItemMargin; - if (right) - *right = rightLayoutItemMargin; - if (bottom) - *bottom = bottomLayoutItemMargin; + // Remove any lazily allocated data + delete[] margins; + delete[] windowFrameMargins; + delete windowData; } -void QGraphicsWidgetPrivate::setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom) -{ - if (leftLayoutItemMargin == left - && topLayoutItemMargin == top - && rightLayoutItemMargin == right - && bottomLayoutItemMargin == bottom) - return; +/*! + \internal - Q_Q(QGraphicsWidget); - leftLayoutItemMargin = left; - topLayoutItemMargin = top; - rightLayoutItemMargin = right; - bottomLayoutItemMargin = bottom; - q->updateGeometry(); + Ensures that margins is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsWidgetPrivate::ensureMargins() const +{ + if (!margins) { + margins = new qreal[4]; + for (int i = 0; i < 4; ++i) + margins[i] = 0; + } } -void QGraphicsWidgetPrivate::setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt) +/*! + \internal + + Ensures that windowFrameMargins is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsWidgetPrivate::ensureWindowFrameMargins() const { - Q_Q(QGraphicsWidget); - QStyleOption myOpt; - if (!opt) { - q->initStyleOption(&myOpt); - myOpt.rect.setRect(0, 0, 32768, 32768); // arbitrary - opt = &myOpt; + if (!windowFrameMargins) { + windowFrameMargins = new qreal[4]; + for (int i = 0; i < 4; ++i) + windowFrameMargins[i] = 0; } +} - QRect liRect = q->style()->subElementRect(element, opt, /* q */ 0); - if (liRect.isValid()) { - leftLayoutItemMargin = (opt->rect.left() - liRect.left()); - topLayoutItemMargin = (opt->rect.top() - liRect.top()); - rightLayoutItemMargin = (liRect.right() - opt->rect.right()); - bottomLayoutItemMargin = (liRect.bottom() - opt->rect.bottom()); - } else { - leftLayoutItemMargin = 0; - topLayoutItemMargin = 0; - rightLayoutItemMargin = 0; - bottomLayoutItemMargin = 0; - } +/*! + \internal + + Ensures that windowData is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsWidgetPrivate::ensureWindowData() +{ + if (!windowData) + windowData = new WindowData; } void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette) @@ -297,11 +301,12 @@ QFont QGraphicsWidgetPrivate::naturalWidgetFont() const void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *option) { Q_Q(QGraphicsWidget); + ensureWindowData(); q->initStyleOption(option); option->rect.setHeight(titleBarHeight(*option)); option->titleBarFlags = windowFlags; option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu; - option->activeSubControls = hoveredSubControl; + option->activeSubControls = windowData->hoveredSubControl; bool isActive = q->isActiveWindow(); if (isActive) { option->state |= QStyle::State_Active; @@ -313,7 +318,8 @@ void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *optio } QFont windowTitleFont = QApplication::font("QWorkspaceTitleBar"); QRect textRect = q->style()->subControlRect(QStyle::CC_TitleBar, option, QStyle::SC_TitleBarLabel, 0); - option->text = QFontMetrics(windowTitleFont).elidedText(windowTitle, Qt::ElideRight, textRect.width()); + option->text = QFontMetrics(windowTitleFont).elidedText( + windowData->windowTitle, Qt::ElideRight, textRect.width()); } void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags) @@ -341,9 +347,10 @@ void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags) void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); - if (grabbedSection != Qt::NoSection) { - if (grabbedSection == Qt::TitleBarArea) { - buttonSunken = false; + ensureWindowData(); + if (windowData->grabbedSection != Qt::NoSection) { + if (windowData->grabbedSection == Qt::TitleBarArea) { + windowData->buttonSunken = false; QStyleOptionTitleBar bar; initStyleOptionTitleBar(&bar); // make sure that the coordinates (rect and pos) we send to the style are positive. @@ -351,8 +358,10 @@ void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEve bar.rect.moveTo(0,0); bar.rect.setHeight(q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &bar)); QPointF pos = event->pos(); - pos.rx() += leftWindowFrameMargin; - pos.ry() += topWindowFrameMargin; + if (windowFrameMargins) { + pos.rx() += windowFrameMargins[Left]; + pos.ry() += windowFrameMargins[Top]; + } bar.subControls = QStyle::SC_TitleBarCloseButton; if (q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, @@ -361,7 +370,7 @@ void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEve } } if (!(static_cast<QGraphicsSceneMouseEvent *>(event)->buttons())) - grabbedSection = Qt::NoSection; + windowData->grabbedSection = Qt::NoSection; event->accept(); } } @@ -372,35 +381,16 @@ void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent if (event->button() != Qt::LeftButton) return; - startGeometry = q->geometry(); - grabbedSection = q->windowFrameSectionAt(event->pos()); - switch (grabbedSection) { - case Qt::LeftSection: - case Qt::TopLeftSection: - mouseDelta = event->pos() - q->rect().topLeft(); - break; - case Qt::TopSection: - case Qt::TopRightSection: - mouseDelta = event->pos() - q->rect().topRight(); - break; - case Qt::RightSection: - case Qt::BottomRightSection: - mouseDelta = event->pos() - q->rect().bottomRight(); - break; - case Qt::BottomSection: - case Qt::BottomLeftSection: - mouseDelta = event->pos() - q->rect().bottomLeft(); - break; - case Qt::TitleBarArea: - if (hoveredSubControl == QStyle::SC_TitleBarCloseButton) { - buttonSunken = true; - q->update(); - } - break; - case Qt::NoSection: - break; + ensureWindowData(); + windowData->startGeometry = q->geometry(); + windowData->grabbedSection = q->windowFrameSectionAt(event->pos()); + ensureWindowData(); + if (windowData->grabbedSection == Qt::TitleBarArea + && windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton) { + windowData->buttonSunken = true; + q->update(); } - event->setAccepted(grabbedSection != Qt::NoSection); + event->setAccepted(windowData->grabbedSection != Qt::NoSection); } static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, @@ -455,7 +445,8 @@ static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); - if (!(event->buttons() & Qt::LeftButton) || hoveredSubControl != QStyle::SC_TitleBarLabel) + ensureWindowData(); + if (!(event->buttons() & Qt::LeftButton) || windowData->hoveredSubControl != QStyle::SC_TitleBarLabel) return; QLineF delta(q->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)), event->pos()); @@ -464,49 +455,56 @@ void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent QLineF parentYDelta(q->mapToParent(QPointF(0, delta.p1().y())), q->mapToParent(QPointF(0, delta.p2().y()))); QRectF newGeometry; - switch (grabbedSection) { + switch (windowData->grabbedSection) { case Qt::LeftSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), - startGeometry.size() - QSizeF(delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentXDelta.dx(), parentXDelta.dy()), + windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy())); break; case Qt::TopLeftSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), - startGeometry.size() - QSizeF(delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentDelta.dx(), parentDelta.dy()), + windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy())); break; case Qt::TopSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), - startGeometry.size() - QSizeF(0, delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentYDelta.dx(), parentYDelta.dy()), + windowData->startGeometry.size() - QSizeF(0, delta.dy())); break; case Qt::TopRightSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), - startGeometry.size() - QSizeF(-delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentYDelta.dx(), parentYDelta.dy()), + windowData->startGeometry.size() - QSizeF(-delta.dx(), delta.dy())); break; case Qt::RightSection: - newGeometry = QRectF(startGeometry.topLeft(), - startGeometry.size() + QSizeF(delta.dx(), 0)); + newGeometry = QRectF(windowData->startGeometry.topLeft(), + windowData->startGeometry.size() + QSizeF(delta.dx(), 0)); break; case Qt::BottomRightSection: - newGeometry = QRectF(startGeometry.topLeft(), - startGeometry.size() + QSizeF(delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft(), + windowData->startGeometry.size() + QSizeF(delta.dx(), delta.dy())); break; case Qt::BottomSection: - newGeometry = QRectF(startGeometry.topLeft(), - startGeometry.size() + QSizeF(0, delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft(), + windowData->startGeometry.size() + QSizeF(0, delta.dy())); break; case Qt::BottomLeftSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), - startGeometry.size() - QSizeF(delta.dx(), -delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentXDelta.dx(), parentXDelta.dy()), + windowData->startGeometry.size() - QSizeF(delta.dx(), -delta.dy())); break; case Qt::TitleBarArea: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), - startGeometry.size()); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentDelta.dx(), parentDelta.dy()), + windowData->startGeometry.size()); break; case Qt::NoSection: break; } - if (grabbedSection != Qt::NoSection) { - _q_boundGeometryToSizeConstraints(startGeometry, &newGeometry, grabbedSection, + if (windowData->grabbedSection != Qt::NoSection) { + _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry, + windowData->grabbedSection, q->effectiveSizeHint(Qt::MinimumSize), q->effectiveSizeHint(Qt::MaximumSize)); q->setGeometry(newGeometry); @@ -519,21 +517,25 @@ void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent if (!hasDecoration()) return; + ensureWindowData(); + if (q->rect().contains(event->pos())) { - if (buttonMouseOver || hoveredSubControl != QStyle::SC_None) + if (windowData->buttonMouseOver || windowData->hoveredSubControl != QStyle::SC_None) windowFrameHoverLeaveEvent(event); return; } - bool wasMouseOver = buttonMouseOver; - QRect oldButtonRect = buttonRect; - buttonRect = QRect(); - buttonMouseOver = false; + bool wasMouseOver = windowData->buttonMouseOver; + QRect oldButtonRect = windowData->buttonRect; + windowData->buttonRect = QRect(); + windowData->buttonMouseOver = false; QPointF pos = event->pos(); QStyleOptionTitleBar bar; // make sure that the coordinates (rect and pos) we send to the style are positive. - pos.rx() += leftWindowFrameMargin; - pos.ry() += topWindowFrameMargin; + if (windowFrameMargins) { + pos.rx() += windowFrameMargins[Left]; + pos.ry() += windowFrameMargins[Top]; + } initStyleOptionTitleBar(&bar); bar.rect = q->windowFrameRect().toRect(); bar.rect.moveTo(0,0); @@ -559,14 +561,17 @@ void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent cursorShape = Qt::SizeVerCursor; break; case Qt::TitleBarArea: - buttonRect = q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0); + windowData->buttonRect = q->style()->subControlRect( + QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0); #ifdef Q_WS_MAC // On mac we should hover if we are in the 'area' of the buttons - buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0); - buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0); + windowData->buttonRect |= q->style()->subControlRect( + QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0); + windowData->buttonRect |= q->style()->subControlRect( + QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0); #endif - if (buttonRect.contains(pos.toPoint())) - buttonMouseOver = true; + if (windowData->buttonRect.contains(pos.toPoint())) + windowData->buttonMouseOver = true; event->ignore(); break; default: @@ -578,15 +583,15 @@ void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent q->setCursor(cursorShape); #endif // update buttons if we hover over them - hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0); - if (hoveredSubControl != QStyle::SC_TitleBarCloseButton) - hoveredSubControl = QStyle::SC_TitleBarLabel; + windowData->hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0); + if (windowData->hoveredSubControl != QStyle::SC_TitleBarCloseButton) + windowData->hoveredSubControl = QStyle::SC_TitleBarLabel; - if (buttonMouseOver != wasMouseOver) { + if (windowData->buttonMouseOver != wasMouseOver) { if (!oldButtonRect.isNull()) q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft())); - if (!buttonRect.isNull()) - q->update(QRectF(buttonRect).translated(q->windowFrameRect().topLeft())); + if (!windowData->buttonRect.isNull()) + q->update(QRectF(windowData->buttonRect).translated(q->windowFrameRect().topLeft())); } } @@ -600,16 +605,19 @@ void QGraphicsWidgetPrivate::windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent q->unsetCursor(); #endif + ensureWindowData(); + bool needsUpdate = false; - if (hoveredSubControl == QStyle::SC_TitleBarCloseButton || buttonMouseOver) + if (windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton + || windowData->buttonMouseOver) needsUpdate = true; // update the hover state (of buttons etc...) - hoveredSubControl = QStyle::SC_None; - buttonMouseOver = false; - buttonRect = QRect(); + windowData->hoveredSubControl = QStyle::SC_None; + windowData->buttonMouseOver = false; + windowData->buttonRect = QRect(); if (needsUpdate) - q->update(buttonRect); + q->update(windowData->buttonRect); } } @@ -618,34 +626,6 @@ bool QGraphicsWidgetPrivate::hasDecoration() const return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint); } -/*! - \internal -*/ -void QGraphicsWidgetPrivate::setFocusWidget() -{ - // Update focus child chain. - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); - QGraphicsWidget *parent = widget; - bool hidden = !visible; - do { - parent->d_func()->focusChild = widget; - } while (!parent->isWindow() && (parent = parent->parentWidget()) && (!hidden || !parent->d_func()->visible)); -} - -/*! - \internal -*/ -void QGraphicsWidgetPrivate::clearFocusWidget() -{ - // Reset focus child chain. - QGraphicsWidget *parent = static_cast<QGraphicsWidget *>(q_ptr); - do { - if (parent->d_func()->focusChild != q_ptr) - break; - parent->d_func()->focusChild = 0; - } while (!parent->isWindow() && (parent = parent->parentWidget())); -} - /** * is called after a reparent has taken place to fix up the focus chain(s) */ @@ -662,12 +642,6 @@ void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *new QGraphicsWidget *firstOld = 0; bool wasPreviousNew = true; - - if (focusChild) { - // Ensure that the current focus child doesn't leave pointers around - // before reparenting. - focusChild->clearFocus(); - } while (w != q) { bool isCurrentNew = q->isAncestorOf(w); diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h index 3c0dd31..92e520a 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.h +++ b/src/gui/graphicsview/qgraphicswidget_p.h @@ -68,19 +68,12 @@ class QStyleOptionTitleBar; #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW -class Q_GUI_EXPORT QGraphicsWidgetPrivate : public QGraphicsItemPrivate +class QGraphicsWidgetPrivate : public QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsWidget) public: QGraphicsWidgetPrivate() - : leftMargin(0), - topMargin(0), - rightMargin(0), - bottomMargin(0), - leftLayoutItemMargin(0), - topLayoutItemMargin(0), - rightLayoutItemMargin(0), - bottomLayoutItemMargin(0), + : margins(0), layout(0), inheritedPaletteResolveMask(0), inheritedFontResolveMask(0), @@ -90,42 +83,24 @@ public: focusPolicy(Qt::NoFocus), focusNext(0), focusPrev(0), - focusChild(0), windowFlags(0), - hoveredSubControl(QStyle::SC_None), - grabbedSection(Qt::NoSection), - buttonMouseOver(false), - buttonSunken(false), + windowData(0), setWindowFrameMargins(false), - leftWindowFrameMargin(0), - topWindowFrameMargin(0), - rightWindowFrameMargin(0), - bottomWindowFrameMargin(0) + windowFrameMargins(0) { } + virtual ~QGraphicsWidgetPrivate(); void init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags); qreal titleBarHeight(const QStyleOptionTitleBar &options) const; // Margins - qreal leftMargin; - qreal topMargin; - qreal rightMargin; - qreal bottomMargin; - QRectF contentsRect; - - // Layout item margins - void getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; - void setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom); - void setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt = 0); + enum {Left, Top, Right, Bottom}; + mutable qreal *margins; + void ensureMargins() const; void fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *newScene = 0); void setLayout_helper(QGraphicsLayout *l); - qreal leftLayoutItemMargin; - qreal topLayoutItemMargin; - qreal rightLayoutItemMargin; - qreal bottomLayoutItemMargin; - // Layouts QGraphicsLayout *layout; void setLayoutDirection_helper(Qt::LayoutDirection direction); @@ -202,26 +177,29 @@ public: Qt::FocusPolicy focusPolicy; QGraphicsWidget *focusNext; QGraphicsWidget *focusPrev; - QGraphicsWidget *focusChild; - void setFocusWidget(); - void clearFocusWidget(); // Windows Qt::WindowFlags windowFlags; - QString windowTitle; - QStyle::SubControl hoveredSubControl; - Qt::WindowFrameSection grabbedSection; - uint buttonMouseOver : 1; - uint buttonSunken : 1; - QPointF mouseDelta; // to compensate for small error when interactively resizing - QRectF startGeometry; - QRect buttonRect; + struct WindowData { + QString windowTitle; + QStyle::SubControl hoveredSubControl; + Qt::WindowFrameSection grabbedSection; + uint buttonMouseOver : 1; + uint buttonSunken : 1; + QRectF startGeometry; + QRect buttonRect; + WindowData() + : hoveredSubControl(QStyle::SC_None) + , grabbedSection(Qt::NoSection) + , buttonMouseOver(false) + , buttonSunken(false) + {} + } *windowData; + void ensureWindowData(); bool setWindowFrameMargins; - qreal leftWindowFrameMargin; - qreal topWindowFrameMargin; - qreal rightWindowFrameMargin; - qreal bottomWindowFrameMargin; + mutable qreal *windowFrameMargins; + void ensureWindowFrameMargins() const; #ifndef QT_NO_ACTION QList<QAction *> actions; diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index c3ba17c..630b23a 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -275,7 +275,7 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz if (hasIgnoreFlag) { factors[i] = (stretch < 0) ? 1.0 : 0.0; } else { - factors[i] = (stretch < 0) ? sizes[i] : 0.0; + factors[i] = (stretch < 0) ? sizes[i] : 0.0; } } else if (stretch == sumStretches) { factors[i] = 1.0; @@ -615,7 +615,7 @@ QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal heig QSizeF size = effectiveMaxSize().boundedTo(QSizeF(cellWidth, cellHeight)); width = size.width(); height = size.height(); - + Qt::Alignment align = q_engine->effectiveAlignment(this); switch (align & Qt::AlignHorizontal_Mask) { case Qt::AlignHCenter: @@ -717,7 +717,7 @@ void QGridLayoutItem::dump(int indent) const void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta) { count += delta; - + insertOrRemoveItems(stretches, row, delta); insertOrRemoveItems(spacings, row, delta); insertOrRemoveItems(alignments, row, delta); @@ -1076,7 +1076,7 @@ QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHi break; } return QSizeF(); -} +} QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const { @@ -1131,9 +1131,9 @@ void QGridLayoutEngine::dump(int indent) const QString message = QLatin1String("[ "); for (int column = 0; column < internalGridColumnCount(); ++column) { message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3); - message += QLatin1String(" "); + message += QLatin1Char(' '); } - message += QLatin1String("]"); + message += QLatin1Char(']'); qDebug("%*s %s", indent, "", qPrintable(message)); } @@ -1150,16 +1150,16 @@ void QGridLayoutEngine::dump(int indent) const q_rowData.dump(indent + 2); qDebug("%*s Geometries output", indent, ""); + QVector<qreal> *cellPos = &q_yy; for (int pass = 0; pass < 2; ++pass) { - QVector<qreal> &cellPos = q_yy; QString message; - for (i = 0; i < cellPos.count(); ++i) { + for (i = 0; i < cellPos->count(); ++i) { message += QLatin1String((message.isEmpty() ? "[" : ", ")); - message += QString::number(cellPos.at(i)); + message += QString::number(cellPos->at(i)); } - message += QLatin1String("]"); + message += QLatin1Char(']'); qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message)); - cellPos = q_xx; + cellPos = &q_xx; } } #endif @@ -1538,5 +1538,5 @@ void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo, } QT_END_NAMESPACE - + #endif //QT_NO_GRAPHICSVIEW |