diff options
Diffstat (limited to 'src/widgets/graphicsview/qgraphicsview.cpp')
-rw-r--r-- | src/widgets/graphicsview/qgraphicsview.cpp | 425 |
1 files changed, 218 insertions, 207 deletions
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index 1db7bf222d..9505e2529a 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50; @@ -97,7 +61,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 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 - during a transformation. Because of the scene alignment (setAligment()), + during a transformation. Because of the scene alignment (setAlignment()), translating the view will have no visual impact. You can interact with the items on the scene by using the mouse and @@ -107,7 +71,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < the events and reacts to them. For example, if you click on a selectable item, the item will typically let the scene know that it has been selected, and it will also redraw itself to display a selection - rectangle. Similiary, if you click and drag the mouse to move a movable + rectangle. Similarly, if you click and drag the mouse to move a movable item, it's the item that handles the mouse moves and moves itself. Item interaction is enabled by default, and you can toggle it by calling setInteractive(). @@ -121,6 +85,16 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < view coordinates and scene coordinates, and to find items on the scene using view coordinates. + When using a QOpenGLWidget as a viewport, stereoscopic rendering is + supported. This is done using the same pattern as QOpenGLWidget::paintGL. + To enable it, enable the QSurfaceFormat::StereoBuffers flag. Because of + how the flag is handled internally, set QSurfaceFormat::StereoBuffers flag + globally before the window is created using QSurfaceFormat::setDefaultFormat(). + If the flag is enabled and there is hardware support for stereoscopic + rendering, then drawBackground() and drawForeground() will be triggered twice + each frame. Call QOpenGLWidget::currentTargetBuffer() to query which buffer + is currently being drawn to. + \image graphicsview-view.png \note Using an OpenGL viewport limits the ability to use QGraphicsProxyWidget. @@ -294,6 +268,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < #include <QtWidgets/qstyleoption.h> #include <private/qevent_p.h> +#include <QtGui/private/qeventpoint_p.h> QT_BEGIN_NAMESPACE @@ -314,7 +289,7 @@ void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEv auto &pt = touchEvent->point(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 - QMutableEventPoint::from(pt).setScenePosition(d->mapToScene(pt.position())); + QMutableEventPoint::setScenePosition(pt, d->mapToScene(pt.position())); // screenPos, startScreenPos, and lastScreenPos are already set } } @@ -340,7 +315,6 @@ QGraphicsViewPrivate::QGraphicsViewPrivate() hasUpdateClip(false), mousePressButton(Qt::NoButton), leftIndent(0), topIndent(0), - lastMouseEvent(QEvent::None, QPointF(), QPointF(), QPointF(), Qt::NoButton, { }, { }), alignment(Qt::AlignCenter), transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor), viewportUpdateMode(QGraphicsView::MinimalViewportUpdate), @@ -371,10 +345,10 @@ void QGraphicsViewPrivate::recalculateContentSize() { Q_Q(QGraphicsView); - QSize maxSize = q->maximumViewportSize(); + const QSize maxSize = q->maximumViewportSize(); int width = maxSize.width(); int height = maxSize.height(); - QRectF viewRect = matrix.mapRect(q->sceneRect()); + const QRectF viewRect = matrix.mapRect(q->sceneRect()); bool frameOnlyAround = (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, nullptr, q)); if (frameOnlyAround) { @@ -386,9 +360,8 @@ void QGraphicsViewPrivate::recalculateContentSize() // Adjust the maximum width and height of the viewport based on the width // of visible scroll bars. - int scrollBarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, nullptr, q); - if (frameOnlyAround) - scrollBarExtent += frameWidth * 2; + const int scrollBarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, nullptr, q) + + (frameOnlyAround ? frameWidth * 2 : 0); // We do not need to subtract the width scrollbars whose policy is // Qt::ScrollBarAlwaysOn, this was already done by maximumViewportSize(). @@ -410,62 +383,70 @@ void QGraphicsViewPrivate::recalculateContentSize() // Setting the ranges of these scroll bars can/will cause the values to // change, and scrollContentsBy() will be called correspondingly. This // will reset the last center point. - QPointF savedLastCenterPoint = lastCenterPoint; + const QPointF savedLastCenterPoint = lastCenterPoint; // Remember the former indent settings - qreal oldLeftIndent = leftIndent; - qreal oldTopIndent = topIndent; + const qreal oldLeftIndent = leftIndent; + const qreal oldTopIndent = topIndent; // If the whole scene fits horizontally, we center the scene horizontally, // and ignore the horizontal scroll bars. - int left = q_round_bound(viewRect.left()); - int right = q_round_bound(viewRect.right() - width); + const int left = q_round_bound(viewRect.left()); + const int right = q_round_bound(viewRect.right() - width); if (left >= right) { - hbar->setRange(0, 0); - switch (alignment & Qt::AlignHorizontal_Mask) { case Qt::AlignLeft: leftIndent = -viewRect.left(); break; case Qt::AlignRight: - leftIndent = width - viewRect.width() - viewRect.left() - 1; + leftIndent = maxSize.width() - viewRect.width() - viewRect.left() - 1; break; case Qt::AlignHCenter: default: - leftIndent = width / 2 - (viewRect.left() + viewRect.right()) / 2; + leftIndent = maxSize.width() / 2 - (viewRect.left() + viewRect.right()) / 2; break; } + + hbar->setRange(0, 0); } else { + leftIndent = 0; + hbar->setRange(left, right); hbar->setPageStep(width); hbar->setSingleStep(width / 20); - leftIndent = 0; + + if (oldLeftIndent != 0) + hbar->setValue(-oldLeftIndent); } // If the whole scene fits vertically, we center the scene vertically, and // ignore the vertical scroll bars. - int top = q_round_bound(viewRect.top()); - int bottom = q_round_bound(viewRect.bottom() - height); + const int top = q_round_bound(viewRect.top()); + const int bottom = q_round_bound(viewRect.bottom() - height); if (top >= bottom) { - vbar->setRange(0, 0); - switch (alignment & Qt::AlignVertical_Mask) { case Qt::AlignTop: topIndent = -viewRect.top(); break; case Qt::AlignBottom: - topIndent = height - viewRect.height() - viewRect.top() - 1; + topIndent = maxSize.height() - viewRect.height() - viewRect.top() - 1; break; case Qt::AlignVCenter: default: - topIndent = height / 2 - (viewRect.top() + viewRect.bottom()) / 2; + topIndent = maxSize.height() / 2 - (viewRect.top() + viewRect.bottom()) / 2; break; } + + vbar->setRange(0, 0); } else { + topIndent = 0; + vbar->setRange(top, bottom); vbar->setPageStep(height); vbar->setSingleStep(height / 20); - topIndent = 0; + + if (oldTopIndent != 0) + vbar->setValue(-oldTopIndent); } // Restorethe center point from before the ranges changed. @@ -502,8 +483,8 @@ void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor) if (q->underMouse()) { // Last scene pos: lastMouseMoveScenePoint // Current mouse pos: - QPointF transformationDiff = q->mapToScene(viewport->rect().center()) - - q->mapToScene(viewport->mapFromGlobal(QCursor::pos())); + QPointF transformationDiff = mapToScene(viewport->rect().toRectF().center()) + - mapToScene(viewport->mapFromGlobal(QCursor::pos().toPointF())); q->centerOn(lastMouseMoveScenePoint + transformationDiff); } else { q->centerOn(lastCenterPoint); @@ -523,8 +504,7 @@ void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor) */ void QGraphicsViewPrivate::updateLastCenterPoint() { - Q_Q(QGraphicsView); - lastCenterPoint = q->mapToScene(viewport->rect().center()); + lastCenterPoint = mapToScene(viewport->rect().toRectF().center()); } /*! @@ -615,7 +595,7 @@ bool QGraphicsViewPrivate::canStartScrollingAt(const QPoint &startPos) const const QGraphicsItem *childItem = q->itemAt(startPos); - if (childItem && (childItem->flags() & QGraphicsItem::ItemIsMovable)) + if (!startPos.isNull() && childItem && (childItem->flags() & QGraphicsItem::ItemIsMovable)) return false; return QAbstractScrollAreaPrivate::canStartScrollingAt(startPos); @@ -628,7 +608,8 @@ void QGraphicsViewPrivate::replayLastMouseEvent() { if (!useLastMouseEvent || !scene) return; - mouseMoveEventHandler(&lastMouseEvent); + QSinglePointEvent *spe = static_cast<QSinglePointEvent *>(&lastMouseEvent); + mouseMoveEventHandler(static_cast<QMouseEvent *>(spe)); } /*! @@ -637,8 +618,7 @@ void QGraphicsViewPrivate::replayLastMouseEvent() void QGraphicsViewPrivate::storeMouseEvent(QMouseEvent *event) { useLastMouseEvent = true; - lastMouseEvent = QMouseEvent(QEvent::MouseMove, event->position(), event->scenePosition(), event->globalPosition(), - event->button(), event->buttons(), event->modifiers()); + lastMouseEvent = *event; } void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) @@ -672,6 +652,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) mouseEvent.setModifiers(event->modifiers()); mouseEvent.setSource(event->source()); mouseEvent.setFlags(event->flags()); + mouseEvent.setTimestamp(event->timestamp()); lastMouseMoveScenePoint = mouseEvent.scenePos(); lastMouseMoveScreenPoint = mouseEvent.screenPos(); mouseEvent.setAccepted(false); @@ -701,7 +682,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) mouseEvent.widget()); } // Find the topmost item under the mouse with a cursor. - for (QGraphicsItem *item : qAsConst(scene->d_func()->cachedItemsUnderMouse)) { + for (QGraphicsItem *item : std::as_const(scene->d_func()->cachedItemsUnderMouse)) { if (item->isEnabled() && item->hasCursor()) { _q_setViewportCursor(item->cursor()); return; @@ -731,7 +712,7 @@ QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRec option.shape = QRubberBand::Rectangle; QRegion tmp; - tmp += rect; + tmp += rect.adjusted(-1, -1, 1, 1); if (widget->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, widget, &mask)) tmp &= mask.region; return tmp; @@ -874,6 +855,7 @@ void QGraphicsViewPrivate::storeDragDropEvent(const QGraphicsSceneDragDropEvent lastDragDropEvent->setMimeData(event->mimeData()); lastDragDropEvent->setWidget(event->widget()); lastDragDropEvent->setSource(event->source()); + lastDragDropEvent->setTimestamp(event->timestamp()); } /*! @@ -1576,7 +1558,7 @@ void QGraphicsView::setRubberBandSelectionMode(Qt::ItemSelectionMode mode) is currently doing an itemselection with rubber band. When the user is not using the rubber band this functions returns (a null) QRectF(). - Notice that part of this QRect can be outise the visual viewport. It can e.g + Notice that part of this QRect can be outside the visual viewport. It can e.g contain negative values. \sa rubberBandSelectionMode, rubberBandChanged() @@ -1754,7 +1736,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) QEvent windowDeactivate(QEvent::WindowDeactivate); QCoreApplication::sendEvent(d->scene, &windowDeactivate); } - if(hasFocus()) + if (hasFocus()) d->scene->clearFocus(); } @@ -1909,14 +1891,14 @@ void QGraphicsView::centerOn(const QPointF &pos) qint64 horizontal = 0; horizontal += horizontalScrollBar()->minimum(); horizontal += horizontalScrollBar()->maximum(); - horizontal -= int(viewPoint.x() - width / 2.0); + horizontal -= qRound(viewPoint.x() - width / 2.0); horizontalScrollBar()->setValue(horizontal); } else { - horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0)); + horizontalScrollBar()->setValue(qRound(viewPoint.x() - width / 2.0)); } } if (!d->topIndent) - verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0)); + verticalScrollBar()->setValue(qRound(viewPoint.y() - height / 2.0)); d->lastCenterPoint = oldCenterPoint; } @@ -2360,7 +2342,7 @@ QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const if (!d->scene) return nullptr; const QList<QGraphicsItem *> itemsAtPos = items(pos); - return itemsAtPos.isEmpty() ? 0 : itemsAtPos.first(); + return itemsAtPos.isEmpty() ? nullptr : itemsAtPos.first(); } /*! @@ -2447,7 +2429,7 @@ QPolygonF QGraphicsView::mapToScene(const QRect &rect) const QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const { QPolygonF poly; - poly.reserve(polygon.count()); + poly.reserve(polygon.size()); for (const QPoint &point : polygon) poly << mapToScene(point); return poly; @@ -2543,7 +2525,7 @@ QPolygon QGraphicsView::mapFromScene(const QRectF &rect) const QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const { QPolygon poly; - poly.reserve(polygon.count()); + poly.reserve(polygon.size()); for (const QPointF &point : polygon) poly << mapFromScene(point); return poly; @@ -2657,7 +2639,7 @@ void QGraphicsView::updateScene(const QList<QRectF> &rects) // Extract and reset dirty scene rect info. QList<QRect> dirtyViewportRects; - dirtyViewportRects.reserve(d->dirtyRegion.rectCount() + rects.count()); + dirtyViewportRects.reserve(d->dirtyRegion.rectCount() + rects.size()); for (const QRect &dirtyRect : d->dirtyRegion) dirtyViewportRects += dirtyRect; d->dirtyRegion = QRegion(); @@ -2686,7 +2668,7 @@ void QGraphicsView::updateScene(const QList<QRectF> &rects) dirtyViewportRects << xrect; } - for (const QRect &rect : qAsConst(dirtyViewportRects)) { + for (const QRect &rect : std::as_const(dirtyViewportRects)) { // Add the exposed rect to the update region. In rect update // mode, we only count the bounding rect of items. if (!boundingRectUpdate) { @@ -2751,6 +2733,9 @@ void QGraphicsView::setupViewport(QWidget *widget) widget->setFocusPolicy(Qt::StrongFocus); + if (isGLWidget) + d->stereoEnabled = QWidgetPrivate::get(widget)->isStereoEnabled(); + if (!isGLWidget) { // autoFillBackground enables scroll acceleration. widget->setAutoFillBackground(true); @@ -2871,11 +2856,10 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.constFirst()); } d->useLastMouseEvent = false; - // a hack to pass a viewport pointer to the scene inside the leave event - Q_ASSERT(event->d == nullptr); - QScopedValueRollback<QEventPrivate *> rb(event->d); - event->d = reinterpret_cast<QEventPrivate *>(viewport()); - QCoreApplication::sendEvent(d->scene, event); + QGraphicsSceneEvent leaveEvent(QEvent::GraphicsSceneLeave); + leaveEvent.setWidget(viewport()); + QCoreApplication::sendEvent(d->scene, &leaveEvent); + event->setAccepted(leaveEvent.isAccepted()); break; } #if QT_CONFIG(tooltip) @@ -2975,6 +2959,7 @@ void QGraphicsView::contextMenuEvent(QContextMenuEvent *event) contextEvent.setModifiers(event->modifiers()); contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason())); contextEvent.setAccepted(event->isAccepted()); + contextEvent.setTimestamp(event->timestamp()); QCoreApplication::sendEvent(d->scene, &contextEvent); event->setAccepted(contextEvent.isAccepted()); } @@ -3190,6 +3175,7 @@ void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) mouseEvent.setModifiers(event->modifiers()); mouseEvent.setSource(event->source()); mouseEvent.setFlags(event->flags()); + mouseEvent.setTimestamp(event->timestamp()); if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else @@ -3241,6 +3227,7 @@ void QGraphicsView::mousePressEvent(QMouseEvent *event) mouseEvent.setSource(event->source()); mouseEvent.setFlags(event->flags()); mouseEvent.setAccepted(false); + mouseEvent.setTimestamp(event->timestamp()); if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else @@ -3364,6 +3351,7 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) mouseEvent.setSource(event->source()); mouseEvent.setFlags(event->flags()); mouseEvent.setAccepted(false); + mouseEvent.setTimestamp(event->timestamp()); if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else @@ -3402,8 +3390,12 @@ void QGraphicsView::wheelEvent(QWheelEvent *event) wheelEvent.setModifiers(event->modifiers()); const bool horizontal = qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y()); wheelEvent.setDelta(horizontal ? event->angleDelta().x() : event->angleDelta().y()); + wheelEvent.setPixelDelta(event->pixelDelta()); + wheelEvent.setPhase(event->phase()); + wheelEvent.setInverted(event->isInverted()); wheelEvent.setOrientation(horizontal ? Qt::Horizontal : Qt::Vertical); wheelEvent.setAccepted(false); + wheelEvent.setTimestamp(event->timestamp()); QCoreApplication::sendEvent(d->scene, &wheelEvent); event->setAccepted(wheelEvent.isAccepted()); if (!event->isAccepted()) @@ -3445,132 +3437,146 @@ void QGraphicsView::paintEvent(QPaintEvent *event) painter.setWorldTransform(viewportTransform()); const QTransform viewTransform = painter.worldTransform(); - // Draw background - if (d->cacheMode & CacheBackground) { - // Recreate the background pixmap, and flag the whole background as - // exposed. - if (d->mustResizeBackgroundPixmap) { - const qreal dpr = d->viewport->devicePixelRatio(); - d->backgroundPixmap = QPixmap(viewport()->size() * dpr); - d->backgroundPixmap.setDevicePixelRatio(dpr); - QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole()); - if (!bgBrush.isOpaque()) - d->backgroundPixmap.fill(Qt::transparent); - QPainter p(&d->backgroundPixmap); - p.fillRect(0, 0, d->backgroundPixmap.width(), d->backgroundPixmap.height(), bgBrush); - d->backgroundPixmapExposed = QRegion(viewport()->rect()); - d->mustResizeBackgroundPixmap = false; - } + const auto actuallyDraw = [&]() { + // Draw background + if (d->cacheMode & CacheBackground) { + // Recreate the background pixmap, and flag the whole background as + // exposed. + if (d->mustResizeBackgroundPixmap) { + const qreal dpr = d->viewport->devicePixelRatio(); + d->backgroundPixmap = QPixmap(viewport()->size() * dpr); + d->backgroundPixmap.setDevicePixelRatio(dpr); + QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole()); + if (!bgBrush.isOpaque()) + d->backgroundPixmap.fill(Qt::transparent); + QPainter p(&d->backgroundPixmap); + p.fillRect(0, 0, d->backgroundPixmap.width(), d->backgroundPixmap.height(), bgBrush); + d->backgroundPixmapExposed = QRegion(viewport()->rect()); + d->mustResizeBackgroundPixmap = false; + } - // Redraw exposed areas - if (!d->backgroundPixmapExposed.isEmpty()) { - QPainter backgroundPainter(&d->backgroundPixmap); - backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip); - if (viewTransformed) - backgroundPainter.setTransform(viewTransform); - QRectF backgroundExposedSceneRect = mapToScene(d->backgroundPixmapExposed.boundingRect()).boundingRect(); - drawBackground(&backgroundPainter, backgroundExposedSceneRect); - d->backgroundPixmapExposed = QRegion(); - } + // Redraw exposed areas + if (!d->backgroundPixmapExposed.isEmpty()) { + QPainter backgroundPainter(&d->backgroundPixmap); + backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip); + if (viewTransformed) + backgroundPainter.setTransform(viewTransform); + QRectF backgroundExposedSceneRect = mapToScene(d->backgroundPixmapExposed.boundingRect()).boundingRect(); + drawBackground(&backgroundPainter, backgroundExposedSceneRect); + d->backgroundPixmapExposed = QRegion(); + } - // Blit the background from the background pixmap - if (viewTransformed) { - painter.setWorldTransform(QTransform()); - painter.drawPixmap(QPoint(), d->backgroundPixmap); - painter.setWorldTransform(viewTransform); + // Blit the background from the background pixmap + if (viewTransformed) { + painter.setWorldTransform(QTransform()); + painter.drawPixmap(QPoint(), d->backgroundPixmap); + painter.setWorldTransform(viewTransform); + } else { + painter.drawPixmap(QPoint(), d->backgroundPixmap); + } } else { - painter.drawPixmap(QPoint(), d->backgroundPixmap); + if (!(d->optimizationFlags & DontSavePainterState)) + painter.save(); + + drawBackground(&painter, exposedSceneRect); + if (!(d->optimizationFlags & DontSavePainterState)) + painter.restore(); } - } else { - if (!(d->optimizationFlags & DontSavePainterState)) - painter.save(); - drawBackground(&painter, exposedSceneRect); - if (!(d->optimizationFlags & DontSavePainterState)) - painter.restore(); - } - // Items - if (!(d->optimizationFlags & IndirectPainting)) { - const quint32 oldRectAdjust = d->scene->d_func()->rectAdjust; - if (d->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) - d->scene->d_func()->rectAdjust = 1; - else - d->scene->d_func()->rectAdjust = 2; - d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : nullptr, - &d->exposedRegion, viewport()); - d->scene->d_func()->rectAdjust = oldRectAdjust; - // Make sure the painter's world transform is restored correctly when - // drawing without painter state protection (DontSavePainterState). - // We only change the worldTransform() so there's no need to do a full-blown - // save() and restore(). Also note that we don't have to do this in case of - // IndirectPainting (the else branch), because in that case we always save() - // and restore() in QGraphicsScene::drawItems(). - if (!d->scene->d_func()->painterStateProtection) - painter.setOpacity(1.0); - painter.setWorldTransform(viewTransform); - } else { - // Make sure we don't have unpolished items before we draw - if (!d->scene->d_func()->unpolishedItems.isEmpty()) - d->scene->d_func()->_q_polishItems(); - // We reset updateAll here (after we've issued polish events) - // so that we can discard update requests coming from polishEvent(). - d->scene->d_func()->updateAll = false; - - // 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); - QTransform transform(Qt::Uninitialized); - for (int i = 0; i < numItems; ++i) { - QGraphicsItem *item = itemArray[i]; - QGraphicsItemPrivate *itemd = item->d_ptr.data(); - itemd->initStyleOption(&styleOptionArray[i], viewTransform, d->exposedRegion, allItems); - // Cache the item's area in view coordinates. - // Note that we have to do this here in case the base class implementation - // (QGraphicsScene::drawItems) is not called. If it is, we'll do this - // operation twice, but that's the price one has to pay for using indirect - // painting :-/. - const QRectF brect = adjustedItemEffectiveBoundingRect(item); - if (!itemd->itemIsUntransformable()) { - transform = item->sceneTransform(); - if (viewTransformed) - transform *= viewTransform; - } else { - transform = item->deviceTransform(viewTransform); + // Items + if (!(d->optimizationFlags & IndirectPainting)) { + const quint32 oldRectAdjust = d->scene->d_func()->rectAdjust; + if (d->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) + d->scene->d_func()->rectAdjust = 1; + else + d->scene->d_func()->rectAdjust = 2; + d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : nullptr, + &d->exposedRegion, viewport()); + d->scene->d_func()->rectAdjust = oldRectAdjust; + // Make sure the painter's world transform is restored correctly when + // drawing without painter state protection (DontSavePainterState). + // We only change the worldTransform() so there's no need to do a full-blown + // save() and restore(). Also note that we don't have to do this in case of + // IndirectPainting (the else branch), because in that case we always save() + // and restore() in QGraphicsScene::drawItems(). + if (!d->scene->d_func()->painterStateProtection) + painter.setOpacity(1.0); + painter.setWorldTransform(viewTransform); + } else { + // Make sure we don't have unpolished items before we draw + if (!d->scene->d_func()->unpolishedItems.isEmpty()) + d->scene->d_func()->_q_polishItems(); + // We reset updateAll here (after we've issued polish events) + // so that we can discard update requests coming from polishEvent(). + d->scene->d_func()->updateAll = false; + + // 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); + QTransform transform(Qt::Uninitialized); + for (int i = 0; i < numItems; ++i) { + QGraphicsItem *item = itemArray[i]; + QGraphicsItemPrivate *itemd = item->d_ptr.data(); + itemd->initStyleOption(&styleOptionArray[i], viewTransform, d->exposedRegion, allItems); + // Cache the item's area in view coordinates. + // Note that we have to do this here in case the base class implementation + // (QGraphicsScene::drawItems) is not called. If it is, we'll do this + // operation twice, but that's the price one has to pay for using indirect + // painting :-/. + const QRectF brect = adjustedItemEffectiveBoundingRect(item); + if (!itemd->itemIsUntransformable()) { + transform = item->sceneTransform(); + if (viewTransformed) + transform *= viewTransform; + } else { + transform = item->deviceTransform(viewTransform); + } + itemd->paintedViewBoundingRects.insert(d->viewport, transform.mapRect(brect).toRect()); } - itemd->paintedViewBoundingRects.insert(d->viewport, transform.mapRect(brect).toRect()); + // Draw the items. + drawItems(&painter, numItems, itemArray, styleOptionArray); + d->freeStyleOptionsArray(styleOptionArray); } - // Draw the items. - drawItems(&painter, numItems, itemArray, styleOptionArray); - d->freeStyleOptionsArray(styleOptionArray); } - } - // Foreground - drawForeground(&painter, exposedSceneRect); + // Foreground + drawForeground(&painter, exposedSceneRect); -#if QT_CONFIG(rubberband) - // Rubberband - if (d->rubberBanding && !d->rubberBandRect.isEmpty()) { - painter.restore(); - QStyleOptionRubberBand option; - option.initFrom(viewport()); - option.rect = d->rubberBandRect; - option.shape = QRubberBand::Rectangle; - - QStyleHintReturnMask mask; - if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, viewport(), &mask)) { - // painter clipping for masked rubberbands - painter.setClipRegion(mask.region, Qt::IntersectClip); + #if QT_CONFIG(rubberband) + // Rubberband + if (d->rubberBanding && !d->rubberBandRect.isEmpty()) { + painter.restore(); + QStyleOptionRubberBand option; + option.initFrom(viewport()); + option.rect = d->rubberBandRect; + option.shape = QRubberBand::Rectangle; + + QStyleHintReturnMask mask; + if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, viewport(), &mask)) { + // painter clipping for masked rubberbands + painter.setClipRegion(mask.region, Qt::IntersectClip); + } + + viewport()->style()->drawControl(QStyle::CE_RubberBand, &option, &painter, viewport()); } + #endif + }; + + actuallyDraw(); - viewport()->style()->drawControl(QStyle::CE_RubberBand, &option, &painter, viewport()); + // For stereo we want to draw everything twice, once to each buffer + if (d->stereoEnabled) { + QWidgetPrivate* w = QWidgetPrivate::get(viewport()); + if (w->toggleStereoTargetBuffer()) { + actuallyDraw(); + w->toggleStereoTargetBuffer(); + } } -#endif painter.end(); @@ -3711,7 +3717,12 @@ void QGraphicsView::drawBackground(QPainter *painter, const QRectF &rect) return; } + const bool wasAa = painter->testRenderHint(QPainter::Antialiasing); + if (wasAa) + painter->setRenderHints(QPainter::Antialiasing, false); painter->fillRect(rect, d->backgroundBrush); + if (wasAa) + painter->setRenderHints(QPainter::Antialiasing, true); } /*! @@ -3743,7 +3754,7 @@ void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect) } /*! - \obsolete + \deprecated Draws the items \a items in the scene using \a painter, after the background and before the foreground are drawn. \a numItems is the number @@ -3815,7 +3826,7 @@ bool QGraphicsView::isTransformed() const otherwise, \a matrix \e replaces the current matrix. \a combine is false by default. - The transformation matrix tranforms the scene into view coordinates. Using + The transformation matrix transforms the scene into view coordinates. Using the default transformation, provided by the identity matrix, one pixel in the view represents one unit in the scene (e.g., a 10x10 rectangular item is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is @@ -3826,13 +3837,13 @@ bool QGraphicsView::isTransformed() const \snippet code/src_gui_graphicsview_qgraphicsview.cpp 7 - To simplify interation with items using a transformed view, QGraphicsView + To simplify interaction with items using a transformed view, QGraphicsView provides mapTo... and mapFrom... functions that can translate between scene and view coordinates. For example, you can call mapToScene() to map - a view coordiate to a floating point scene coordinate, or mapFromScene() + a view coordinate to a floating point scene coordinate, or mapFromScene() to map from floating point scene coordinates to view coordinates. - \sa transform(), rotate(), scale(), shear(), translate() + \sa transform(), resetTransform(), rotate(), scale(), shear(), translate() */ void QGraphicsView::setTransform(const QTransform &matrix, bool combine ) { |