diff options
Diffstat (limited to 'src/gui/graphicsview/qgraphicsitem.cpp')
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 245 |
1 files changed, 193 insertions, 52 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 13b677c..912cfb6 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -186,6 +186,49 @@ high z-values. Stacking order applies to sibling items; parents are always drawn before their children. + \section1 Sorting + + All items are drawn in a defined, stable order, and this same order decides + which items will receive mouse input first when you click on the scene. + Normally you don't have to worry about sorting, as the items follow a + "natural order", following the logical structure of the scene. + + An item's children are stacked on top of the parent, and sibling items are + stacked by insertion order (i.e., in the same order that they were either + added to the scene, or added to the same parent). If you add item A, and + then B, then B will be on top of A. If you then add C, the items' stacking + order will be A, then B, then C. + + \image graphicsview-zorder.png + + This example shows the stacking order of all limbs of the robot from the + \l{graphicsview/dragdroprobot}{Drag and Drop Robot} example. The torso is + the root item (all other items are children or descendants of the torso), + so it is drawn first. Next, the head is drawn, as it is the first item in + the torso's list of children. Then the upper left arm is drawn. As the + lower arm is a child of the upper arm, the lower arm is then drawn, + followed by the upper arm's next sibling, which is the upper right arm, and + so on. + + For advanced users, there are ways to alter how your items are sorted: + + \list + \o You can call setZValue() on an item to explicitly stack it on top of, or + under, other sibling items. The default Z value for an item is 0. Items + with the same Z value are stacked by insertion order. + + \o You can call stackBefore() to reorder the list of children. This will + directly modify the insertion order. + + \o You can set the ItemStacksBehindParent flag to stack a child item behind + its parent. + \endlist + + The stacking order of two sibling items also counts for each item's + children and descendant items. So if one item is on top of another, then + all its children will also be on top of all the other item's children as + well. + \section1 Events QGraphicsItem receives events from QGraphicsScene through the virtual @@ -564,6 +607,21 @@ supportsExtension() and setExtension(). */ +/*! + \enum QGraphicsItem::PanelModality + + This enum specifies the behavior of a modal panel. A modal panel + is one that blocks input to other panels. Note that items that + are children of a modal panel are not blocked. + + The values are: + \value NonModal The panel is not modal and does not block input to other panels. + \value PanelModal The panel is modal to a single item hierarchy and blocks input to its parent pane, all grandparent panels, and all siblings of its parent and grandparent panels. + \value SceneModal The window is modal to the entire scene and blocks input to all panels. + + \sa QGraphicsItem::setPanelModality(), QGraphicsItem::panelModality(), QGraphicsItem::ItemIsPanel +*/ + #include "qgraphicsitem.h" #ifndef QT_NO_GRAPHICSVIEW @@ -1472,10 +1530,12 @@ QList<QGraphicsItem *> QGraphicsItem::children() const /*! \since 4.4 - Returns a list of this item's children. The items are returned in no - particular order. + Returns a list of this item's children. - \sa setParentItem() + The items are sorted by stacking order. This takes into account both the + items' insertion order and their Z-values. + + \sa setParentItem(), zValue(), {QGraphicsItem#Sorting}{Sorting} */ QList<QGraphicsItem *> QGraphicsItem::childItems() const { @@ -1649,6 +1709,16 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) setFlag(ItemStacksBehindParent, d_ptr->z < qreal(0.0)); } + if ((d_ptr->panelModality != NonModal) + && d_ptr->scene + && (flags & ItemIsPanel) != (oldFlags & ItemIsPanel)) { + // update the panel's modal state + if (flags & ItemIsPanel) + d_ptr->scene->d_func()->enterModal(this); + else + d_ptr->scene->d_func()->leaveModal(this); + } + if (d_ptr->scene) { d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true, @@ -1725,6 +1795,87 @@ void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize) update(); } +/*! + \since 4.6 + + Returns the modality for this item. +*/ +QGraphicsItem::PanelModality QGraphicsItem::panelModality() const +{ + return d_ptr->panelModality; +} + +/*! + \since 4.6 + + Sets the modality for this item to \a panelModality. + + Changing the modality of a visible item takes effect immediately. +*/ +void QGraphicsItem::setPanelModality(PanelModality panelModality) +{ + if (d_ptr->panelModality == panelModality) + return; + + PanelModality previousModality = d_ptr->panelModality; + bool enterLeaveModal = (isPanel() && d_ptr->scene && isVisible()); + if (enterLeaveModal && panelModality == NonModal) + d_ptr->scene->d_func()->leaveModal(this); + d_ptr->panelModality = panelModality; + if (enterLeaveModal && d_ptr->panelModality != NonModal) + d_ptr->scene->d_func()->enterModal(this, previousModality); +} + +/*! + \since 4.6 + + Returns true if this item is blocked by a modal panel, false otherwise. If \a blockingPanel is + non-zero, \a blockingPanel will be set to the modal panel that is blocking this item. If this + item is not blocked, \a blockingPanel will not be set by this function. + + This function always returns false for items not in a scene. + + \sa panelModality() setPanelModality() PanelModality +*/ +bool QGraphicsItem::isBlockedByModalPanel(QGraphicsItem **blockingPanel) const +{ + if (!d_ptr->scene) + return false; + + + QGraphicsItem *dummy = 0; + if (!blockingPanel) + blockingPanel = &dummy; + + QGraphicsScenePrivate *scene_d = d_ptr->scene->d_func(); + if (scene_d->modalPanels.isEmpty()) + return false; + + // ### + if (!scene_d->popupWidgets.isEmpty() && scene_d->popupWidgets.first() == this) + return false; + + for (int i = 0; i < scene_d->modalPanels.count(); ++i) { + QGraphicsItem *modalPanel = scene_d->modalPanels.at(i); + if (modalPanel->panelModality() == QGraphicsItem::SceneModal) { + // Scene modal panels block all non-descendents. + if (modalPanel != this && !modalPanel->isAncestorOf(this)) { + *blockingPanel = modalPanel; + return true; + } + } else { + // Window modal panels block ancestors and siblings/cousins. + if (modalPanel != this + && !modalPanel->isAncestorOf(this) + && commonAncestorItem(modalPanel)) { + *blockingPanel = modalPanel; + return true; + } + } + } + return false; +} + #ifndef QT_NO_TOOLTIP /*! Returns the item's tool tip, or an empty QString if no tool tip has been @@ -1934,6 +2085,8 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo q->ungrabMouse(); if (scene->d_func()->keyboardGrabberItems.contains(q)) q->ungrabKeyboard(); + if (q->isPanel() && panelModality != QGraphicsItem::NonModal) + scene->d_func()->leaveModal(q_ptr); } if (q_ptr->hasFocus() && scene) { // Hiding the closest non-panel ancestor of the focus item @@ -1955,10 +2108,15 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo } else { geometryChanged = 1; paintedViewBoundingRectsNeedRepaint = 1; - if (isWidget && scene) { - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); - if (widget->windowType() == Qt::Popup) - scene->d_func()->addPopup(widget); + if (scene) { + if (isWidget) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); + if (widget->windowType() == Qt::Popup) + scene->d_func()->addPopup(widget); + } + if (q->isPanel() && panelModality != QGraphicsItem::NonModal) { + scene->d_func()->enterModal(q_ptr); + } } } @@ -2781,7 +2939,7 @@ bool QGraphicsItem::hasFocus() const 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 + 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. @@ -3309,7 +3467,7 @@ QMatrix QGraphicsItem::matrix() const 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() @@ -3790,7 +3948,7 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) // Update and set the new transformation. d_ptr->setTransformHelper(newTransform); - + // Send post-notification. itemChange(ItemTransformHasChanged, qVariantFromValue<QTransform>(newTransform)); } @@ -3924,7 +4082,7 @@ void QGraphicsItem::scale(qreal sx, qreal sy) /*! \obsolete - Use + Use \code setTransform(QTransform().shear(sh, sv), true); @@ -3946,7 +4104,7 @@ void QGraphicsItem::shear(qreal sh, qreal sv) Use setPos() or setTransformOriginPoint() instead. For identical behavior, use - + \code setTransform(QTransform::fromTranslate(dx, dy), true); \endcode @@ -3987,12 +4145,12 @@ void QGraphicsItem::advance(int phase) } /*! - Returns the Z-value, or the elevation, of the item. The Z-value decides - the stacking order of sibling (neighboring) items. + Returns the Z-value of the item. The Z-value affects the stacking order of + sibling (neighboring) items. The default Z-value is 0. - \sa setZValue() + \sa setZValue(), {QGraphicsItem#Sorting}{Sorting}, stackBefore(), ItemStacksBehindParent */ qreal QGraphicsItem::zValue() const { @@ -4000,33 +4158,18 @@ 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 order of insertion; the - last inserted child is stacked above previous children. - - \img graphicsview-zorder.png + Sets the Z-value of the item to \a z. The Z value decides the stacking + order of sibling (neighboring) items. A sibling item of high Z value will + always be drawn on top of another sibling item with a lower Z value. - Children of different parents are stacked according to the Z-value of - each item's ancestor item which is an immediate child of the two - items' closest common ancestor. For example, a robot item might - define a torso item as the parent of a head item, two arm items, - and two upper-leg items. The upper-leg items would each be parents - of one lower-leg item, and each lower-leg item would be parents of - one foot item. The stacking order of the feet is the same as the - stacking order of each foot's ancestor that is an immediate child - of the two feet's common ancestor (i.e., the torso item); so the - feet are stacked in the same order as the upper-leg items, - regardless of each foot's Z-value. + If you restore the Z value, the item's insertion order will decide its + stacking order. The Z-value does not affect the item's size in any way. The default Z-value is 0. - \sa zValue() + \sa zValue(), {QGraphicsItem#Sorting}{Sorting}, stackBefore(), ItemStacksBehindParent */ void QGraphicsItem::setZValue(qreal z) { @@ -4089,12 +4232,13 @@ void QGraphicsItemPrivate::ensureSequentialSiblingIndex() The \a sibling must have the same Z value as this item, otherwise calling this function will have no effect. - By default, all items are stacked by insertion order (i.e., the first item - you add is drawn before the next item you add). If two items' Z values are - different, then the item with the highest Z value is drawn on top. When the - Z values are the same, the insertion order will decide the stacking order. + By default, all sibling items are stacked by insertion order (i.e., the + first item you add is drawn before the next item you add). If two items' Z + values are different, then the item with the highest Z value is drawn on + top. When the Z values are the same, the insertion order will decide the + stacking order. - \sa setZValue(), ItemStacksBehindParent + \sa setZValue(), ItemStacksBehindParent, {QGraphicsItem#Sorting}{Sorting} */ void QGraphicsItem::stackBefore(const QGraphicsItem *sibling) { @@ -4456,7 +4600,7 @@ bool QGraphicsItem::collidesWithItem(const QGraphicsItem *other, Qt::ItemSelecti Note that this function checks whether the item's shape or bounding rectangle (depending on \a mode) is contained within \a path, and not whether \a path is contained within the items shape - or bounding rectangle. + or bounding rectangle. \sa collidesWithItem(), contains(), shape() */ @@ -5150,14 +5294,8 @@ void QGraphicsItem::update(const QRectF &rect) } while ((item = item->d_ptr->parent)); if (CacheMode(d_ptr->cacheMode) != NoCache) { - QGraphicsItemCache *cache = d_ptr->extraItemCache(); - if (d_ptr->discardUpdateRequest(/* ignoreVisibleBit = */ false, - /* ignoreClipping = */ false, - /* ignoreDirtyBit = */ true)) { - return; - } - // Invalidate cache. + QGraphicsItemCache *cache = d_ptr->extraItemCache(); if (!cache->allExposed) { if (rect.isNull()) { cache->allExposed = true; @@ -5171,6 +5309,9 @@ void QGraphicsItem::update(const QRectF &rect) return; } + if (d_ptr->discardUpdateRequest()) + return; + if (d_ptr->scene) d_ptr->scene->d_func()->markDirty(this, rect); } @@ -6608,7 +6749,7 @@ void QGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) if (d_ptr->isWidget) { // Qt::Popup closes when you click outside. QGraphicsWidget *w = static_cast<QGraphicsWidget *>(this); - if (w->windowFlags() & Qt::Popup) { + if ((w->windowFlags() & Qt::Popup) == Qt::Popup) { event->accept(); if (!w->rect().contains(event->pos())) w->close(); @@ -7033,7 +7174,7 @@ void QGraphicsItem::prepareGeometryChange() // 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->isSignalConnected(scenePrivate->changedSignalIndex) + if (scenePrivate->isSignalConnected(scenePrivate->changedSignalIndex) || scenePrivate->views.isEmpty()) { if (d_ptr->hasTranslateOnlySceneTransform()) { d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), @@ -10537,7 +10678,7 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP effectRect.setRight(deviceWidth - 1); if (bottom + 1 > deviceHeight) effectRect.setBottom(deviceHeight -1); - + } if (effectRect.isEmpty()) |