diff options
Diffstat (limited to 'src/widgets/graphicsview/qgraphicsview.cpp')
-rw-r--r-- | src/widgets/graphicsview/qgraphicsview.cpp | 333 |
1 files changed, 183 insertions, 150 deletions
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index 7bbe5d1608..9505e2529a 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -85,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. @@ -335,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) { @@ -350,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(). @@ -374,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. @@ -466,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); @@ -487,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()); } /*! @@ -666,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; @@ -1542,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() @@ -1875,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; } @@ -2326,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(); } /*! @@ -2413,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; @@ -2509,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; @@ -2623,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(); @@ -2652,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) { @@ -2717,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); @@ -3418,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(); @@ -3810,7 +3843,7 @@ bool QGraphicsView::isTransformed() const 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 ) { |