summaryrefslogtreecommitdiffstats
path: root/src/widgets/graphicsview/qgraphicsview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/graphicsview/qgraphicsview.cpp')
-rw-r--r--src/widgets/graphicsview/qgraphicsview.cpp335
1 files changed, 184 insertions, 151 deletions
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp
index 1498fc7a3a..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();
@@ -3804,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 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 )
{