1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50;
5
6static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 2^9
7
8/*!
9 \class QGraphicsView
10 \brief The QGraphicsView class provides a widget for displaying the
11 contents of a QGraphicsScene.
12 \since 4.2
13 \ingroup graphicsview-api
14 \inmodule QtWidgets
15
16 QGraphicsView visualizes the contents of a QGraphicsScene in a scrollable
17 viewport. To create a scene with geometrical items, see QGraphicsScene's
18 documentation. QGraphicsView is part of the \l{Graphics View Framework}.
19
20 To visualize a scene, you start by constructing a QGraphicsView object,
21 passing the address of the scene you want to visualize to QGraphicsView's
22 constructor. Alternatively, you can call setScene() to set the scene at a
23 later point. After you call show(), the view will by default scroll to the
24 center of the scene and display any items that are visible at this
25 point. For example:
26
27 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 0
28
29 You can explicitly scroll to any position on the scene by using the
30 scroll bars, or by calling centerOn(). By passing a point to centerOn(),
31 QGraphicsView will scroll its viewport to ensure that the point is
32 centered in the view. An overload is provided for scrolling to a
33 QGraphicsItem, in which case QGraphicsView will see to that the center of
34 the item is centered in the view. If all you want is to ensure that a
35 certain area is visible, (but not necessarily centered,) you can call
36 ensureVisible() instead.
37
38 QGraphicsView can be used to visualize a whole scene, or only parts of it.
39 The visualized area is by default detected automatically when the view is
40 displayed for the first time (by calling
41 QGraphicsScene::itemsBoundingRect()). To set the visualized area rectangle
42 yourself, you can call setSceneRect(). This will adjust the scroll bars'
43 ranges appropriately. Note that although the scene supports a virtually
44 unlimited size, the range of the scroll bars will never exceed the range of
45 an integer (INT_MIN, INT_MAX).
46
47 QGraphicsView visualizes the scene by calling render(). By default, the
48 items are drawn onto the viewport by using a regular QPainter, and using
49 default render hints. To change the default render hints that
50 QGraphicsView passes to QPainter when painting items, you can call
51 setRenderHints().
52
53 By default, QGraphicsView provides a regular QWidget for the viewport
54 widget. You can access this widget by calling viewport(), or you can
55 replace it by calling setViewport(). To render using OpenGL, simply call
56 setViewport(new QOpenGLWidget). QGraphicsView takes ownership of the
57 viewport widget.
58
59 QGraphicsView supports affine transformations, using QTransform. You can
60 either pass a matrix to setTransform(), or you can call one of the
61 convenience functions rotate(), scale(), translate() or shear(). The most
62 two common transformations are scaling, which is used to implement
63 zooming, and rotation. QGraphicsView keeps the center of the view fixed
64 during a transformation. Because of the scene alignment (setAlignment()),
65 translating the view will have no visual impact.
66
67 You can interact with the items on the scene by using the mouse and
68 keyboard. QGraphicsView translates the mouse and key events into \e scene
69 events, (events that inherit QGraphicsSceneEvent,), and forward them to
70 the visualized scene. In the end, it's the individual item that handles
71 the events and reacts to them. For example, if you click on a selectable
72 item, the item will typically let the scene know that it has been
73 selected, and it will also redraw itself to display a selection
74 rectangle. Similarly, if you click and drag the mouse to move a movable
75 item, it's the item that handles the mouse moves and moves itself. Item
76 interaction is enabled by default, and you can toggle it by calling
77 setInteractive().
78
79 You can also provide your own custom scene interaction, by creating a
80 subclass of QGraphicsView, and reimplementing the mouse and key event
81 handlers. To simplify how you programmatically interact with items in the
82 view, QGraphicsView provides the mapping functions mapToScene() and
83 mapFromScene(), and the item accessors items() and itemAt(). These
84 functions allow you to map points, rectangles, polygons and paths between
85 view coordinates and scene coordinates, and to find items on the scene
86 using view coordinates.
87
88 When using a QOpenGLWidget as a viewport, stereoscopic rendering is
89 supported. This is done using the same pattern as QOpenGLWidget::paintGL.
90 To enable it, enable the QSurfaceFormat::StereoBuffers flag. Because of
91 how the flag is handled internally, set QSurfaceFormat::StereoBuffers flag
92 globally before the window is created using QSurfaceFormat::setDefaultFormat().
93 If the flag is enabled and there is hardware support for stereoscopic
94 rendering, then drawBackground() and drawForeground() will be triggered twice
95 each frame. Call QOpenGLWidget::currentTargetBuffer() to query which buffer
96 is currently being drawn to.
97
98 \image graphicsview-view.png
99
100 \note Using an OpenGL viewport limits the ability to use QGraphicsProxyWidget.
101 Not all combinations of widgets and styles can be supported with such a setup.
102 You should carefully test your UI and make the necessary adjustments.
103
104 \sa QGraphicsScene, QGraphicsItem, QGraphicsSceneEvent
105*/
106
107/*!
108 \enum QGraphicsView::ViewportAnchor
109
110 This enums describe the possible anchors that QGraphicsView can
111 use when the user resizes the view or when the view is
112 transformed.
113
114 \value NoAnchor No anchor, i.e. the view leaves the scene's
115 position unchanged.
116 \value AnchorViewCenter The scene point at the center of the view
117 is used as the anchor.
118 \value AnchorUnderMouse The point under the mouse is used as the anchor.
119
120 \sa resizeAnchor, transformationAnchor
121*/
122
123/*!
124 \enum QGraphicsView::ViewportUpdateMode
125
126 \since 4.3
127
128 This enum describes how QGraphicsView updates its viewport when the scene
129 contents change or are exposed.
130
131 \value FullViewportUpdate When any visible part of the scene changes or is
132 reexposed, QGraphicsView will update the entire viewport. This approach is
133 fastest when QGraphicsView spends more time figuring out what to draw than
134 it would spend drawing (e.g., when very many small items are repeatedly
135 updated). This is the preferred update mode for viewports that do not
136 support partial updates, such as QOpenGLWidget, and for viewports that
137 need to disable scroll optimization.
138
139 \value MinimalViewportUpdate QGraphicsView will determine the minimal
140 viewport region that requires a redraw, minimizing the time spent drawing
141 by avoiding a redraw of areas that have not changed. This is
142 QGraphicsView's default mode. Although this approach provides the best
143 performance in general, if there are many small visible changes on the
144 scene, QGraphicsView might end up spending more time finding the minimal
145 approach than it will spend drawing.
146
147 \value SmartViewportUpdate QGraphicsView will attempt to find an optimal
148 update mode by analyzing the areas that require a redraw.
149
150 \value BoundingRectViewportUpdate The bounding rectangle of all changes in
151 the viewport will be redrawn. This mode has the advantage that
152 QGraphicsView searches only one region for changes, minimizing time spent
153 determining what needs redrawing. The disadvantage is that areas that have
154 not changed also need to be redrawn.
155
156 \value NoViewportUpdate QGraphicsView will never update its viewport when
157 the scene changes; the user is expected to control all updates. This mode
158 disables all (potentially slow) item visibility testing in QGraphicsView,
159 and is suitable for scenes that either require a fixed frame rate, or where
160 the viewport is otherwise updated externally.
161
162 \sa viewportUpdateMode
163*/
164
165/*!
166 \enum QGraphicsView::OptimizationFlag
167
168 \since 4.3
169
170 This enum describes flags that you can enable to improve rendering
171 performance in QGraphicsView. By default, none of these flags are set.
172 Note that setting a flag usually imposes a side effect, and this effect
173 can vary between paint devices and platforms.
174
175 \value DontSavePainterState When rendering, QGraphicsView protects the
176 painter state (see QPainter::save()) when rendering the background or
177 foreground, and when rendering each item. This allows you to leave the
178 painter in an altered state (i.e., you can call QPainter::setPen() or
179 QPainter::setBrush() without restoring the state after painting). However,
180 if the items consistently do restore the state, you should enable this
181 flag to prevent QGraphicsView from doing the same.
182
183 \value DontAdjustForAntialiasing Disables QGraphicsView's antialiasing
184 auto-adjustment of exposed areas. Items that render antialiased lines on
185 the boundaries of their QGraphicsItem::boundingRect() can end up rendering
186 parts of the line outside. To prevent rendering artifacts, QGraphicsView
187 expands all exposed regions by 2 pixels in all directions. If you enable
188 this flag, QGraphicsView will no longer perform these adjustments,
189 minimizing the areas that require redrawing, which improves performance. A
190 common side effect is that items that do draw with antialiasing can leave
191 painting traces behind on the scene as they are moved.
192
193 \value IndirectPainting Since Qt 4.6, restore the old painting algorithm
194 that calls QGraphicsView::drawItems() and QGraphicsScene::drawItems().
195 To be used only for compatibility with old code.
196*/
197
198/*!
199 \enum QGraphicsView::CacheModeFlag
200
201 This enum describes the flags that you can set for a QGraphicsView's cache
202 mode.
203
204 \value CacheNone All painting is done directly onto the viewport.
205
206 \value CacheBackground The background is cached. This affects both custom
207 backgrounds, and backgrounds based on the backgroundBrush property. When
208 this flag is enabled, QGraphicsView will allocate one pixmap with the full
209 size of the viewport.
210
211 \sa cacheMode
212*/
213
214/*!
215 \enum QGraphicsView::DragMode
216
217 This enum describes the default action for the view when pressing and
218 dragging the mouse over the viewport.
219
220 \value NoDrag Nothing happens; the mouse event is ignored.
221
222 \value ScrollHandDrag The cursor changes into a pointing hand, and
223 dragging the mouse around will scroll the scrolbars. This mode works both
224 in \l{QGraphicsView::interactive}{interactive} and non-interactive mode.
225
226 \value RubberBandDrag A rubber band will appear. Dragging the mouse will
227 set the rubber band geometry, and all items covered by the rubber band are
228 selected. This mode is disabled for non-interactive views.
229
230 \sa dragMode, QGraphicsScene::setSelectionArea()
231*/
232
233/*!
234 \since 5.1
235
236 \fn void QGraphicsView::rubberBandChanged(QRect rubberBandRect, QPointF fromScenePoint, QPointF toScenePoint)
237
238 This signal is emitted when the rubber band rect is changed. The viewport Rect is specified by \a rubberBandRect.
239 The drag start position and drag end position are provided in scene points with \a fromScenePoint and \a toScenePoint.
240
241 When rubberband selection ends this signal will be emitted with null vales.
242
243 \sa rubberBandRect()
244*/
245
246
247#include "qgraphicsview.h"
248#include "qgraphicsview_p.h"
249
250#include "qgraphicsitem.h"
251#include "qgraphicsitem_p.h"
252#include "qgraphicsscene.h"
253#include "qgraphicsscene_p.h"
254#include "qgraphicssceneevent.h"
255#include "qgraphicswidget.h"
256
257#include <QtCore/qdatetime.h>
258#include <QtCore/qdebug.h>
259#include <QtCore/qmath.h>
260#include <QtCore/qscopedvaluerollback.h>
261#include <QtWidgets/qapplication.h>
262#include <QtGui/qevent.h>
263#include <QtWidgets/qlayout.h>
264#include <QtGui/qtransform.h>
265#include <QtGui/qpainter.h>
266#include <QtGui/qpainterpath.h>
267#include <QtWidgets/qscrollbar.h>
268#include <QtWidgets/qstyleoption.h>
269
270#include <private/qevent_p.h>
271#include <QtGui/private/qeventpoint_p.h>
272
273QT_BEGIN_NAMESPACE
274
275bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
276
277inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision
278{
279 if (d <= (qreal) INT_MIN)
280 return INT_MIN;
281 else if (d >= (qreal) INT_MAX)
282 return INT_MAX;
283 return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1);
284}
285
286void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent)
287{
288 for (int i = 0; i < touchEvent->pointCount(); ++i) {
289 auto &pt = touchEvent->point(i);
290 // the scene will set the item local pos, startPos, lastPos, and rect before delivering to
291 // an item, but for now those functions are returning the view's local coordinates
292 QMutableEventPoint::setScenePosition(p&: pt, arg: d->mapToScene(point: pt.position()));
293 // screenPos, startScreenPos, and lastScreenPos are already set
294 }
295}
296
297/*!
298 \internal
299*/
300QGraphicsViewPrivate::QGraphicsViewPrivate()
301 : renderHints(QPainter::TextAntialiasing),
302 dragMode(QGraphicsView::NoDrag),
303 sceneInteractionAllowed(true), hasSceneRect(false),
304 connectedToScene(false),
305 useLastMouseEvent(false),
306 identityMatrix(true),
307 dirtyScroll(true),
308 accelerateScrolling(true),
309 keepLastCenterPoint(true),
310 transforming(false),
311 handScrolling(false),
312 mustAllocateStyleOptions(false),
313 mustResizeBackgroundPixmap(true),
314 fullUpdatePending(true),
315 hasUpdateClip(false),
316 mousePressButton(Qt::NoButton),
317 leftIndent(0), topIndent(0),
318 alignment(Qt::AlignCenter),
319 transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor),
320 viewportUpdateMode(QGraphicsView::MinimalViewportUpdate),
321 scene(nullptr),
322#if QT_CONFIG(rubberband)
323 rubberBanding(false),
324 rubberBandSelectionMode(Qt::IntersectsItemShape),
325 rubberBandSelectionOperation(Qt::ReplaceSelection),
326#endif
327 handScrollMotions(0),
328#ifndef QT_NO_CURSOR
329 hasStoredOriginalCursor(false),
330#endif
331 lastDragDropEvent(nullptr),
332 updateSceneSlotReimplementedChecked(false)
333{
334 styleOptions.reserve(asize: QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS);
335}
336
337QGraphicsViewPrivate::~QGraphicsViewPrivate()
338{
339}
340
341/*!
342 \internal
343*/
344void QGraphicsViewPrivate::recalculateContentSize()
345{
346 Q_Q(QGraphicsView);
347
348 const QSize maxSize = q->maximumViewportSize();
349 int width = maxSize.width();
350 int height = maxSize.height();
351 const QRectF viewRect = matrix.mapRect(q->sceneRect());
352
353 bool frameOnlyAround = (q->style()->styleHint(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: nullptr, widget: q));
354 if (frameOnlyAround) {
355 if (hbarpolicy == Qt::ScrollBarAlwaysOn)
356 height -= frameWidth * 2;
357 if (vbarpolicy == Qt::ScrollBarAlwaysOn)
358 width -= frameWidth * 2;
359 }
360
361 // Adjust the maximum width and height of the viewport based on the width
362 // of visible scroll bars.
363 const int scrollBarExtent = q->style()->pixelMetric(metric: QStyle::PM_ScrollBarExtent, option: nullptr, widget: q)
364 + (frameOnlyAround ? frameWidth * 2 : 0);
365
366 // We do not need to subtract the width scrollbars whose policy is
367 // Qt::ScrollBarAlwaysOn, this was already done by maximumViewportSize().
368 bool useHorizontalScrollBar = (viewRect.width() > width) && hbarpolicy == Qt::ScrollBarAsNeeded;
369 bool useVerticalScrollBar = (viewRect.height() > height) && vbarpolicy == Qt::ScrollBarAsNeeded;
370 if (useHorizontalScrollBar && vbarpolicy == Qt::ScrollBarAsNeeded) {
371 if (viewRect.height() > height - scrollBarExtent)
372 useVerticalScrollBar = true;
373 }
374 if (useVerticalScrollBar && hbarpolicy == Qt::ScrollBarAsNeeded) {
375 if (viewRect.width() > width - scrollBarExtent)
376 useHorizontalScrollBar = true;
377 }
378 if (useHorizontalScrollBar)
379 height -= scrollBarExtent;
380 if (useVerticalScrollBar)
381 width -= scrollBarExtent;
382
383 // Setting the ranges of these scroll bars can/will cause the values to
384 // change, and scrollContentsBy() will be called correspondingly. This
385 // will reset the last center point.
386 const QPointF savedLastCenterPoint = lastCenterPoint;
387
388 // Remember the former indent settings
389 const qreal oldLeftIndent = leftIndent;
390 const qreal oldTopIndent = topIndent;
391
392 // If the whole scene fits horizontally, we center the scene horizontally,
393 // and ignore the horizontal scroll bars.
394 const int left = q_round_bound(d: viewRect.left());
395 const int right = q_round_bound(d: viewRect.right() - width);
396 if (left >= right) {
397 switch (alignment & Qt::AlignHorizontal_Mask) {
398 case Qt::AlignLeft:
399 leftIndent = -viewRect.left();
400 break;
401 case Qt::AlignRight:
402 leftIndent = maxSize.width() - viewRect.width() - viewRect.left() - 1;
403 break;
404 case Qt::AlignHCenter:
405 default:
406 leftIndent = maxSize.width() / 2 - (viewRect.left() + viewRect.right()) / 2;
407 break;
408 }
409
410 hbar->setRange(min: 0, max: 0);
411 } else {
412 leftIndent = 0;
413
414 hbar->setRange(min: left, max: right);
415 hbar->setPageStep(width);
416 hbar->setSingleStep(width / 20);
417
418 if (oldLeftIndent != 0)
419 hbar->setValue(-oldLeftIndent);
420 }
421
422 // If the whole scene fits vertically, we center the scene vertically, and
423 // ignore the vertical scroll bars.
424 const int top = q_round_bound(d: viewRect.top());
425 const int bottom = q_round_bound(d: viewRect.bottom() - height);
426 if (top >= bottom) {
427 switch (alignment & Qt::AlignVertical_Mask) {
428 case Qt::AlignTop:
429 topIndent = -viewRect.top();
430 break;
431 case Qt::AlignBottom:
432 topIndent = maxSize.height() - viewRect.height() - viewRect.top() - 1;
433 break;
434 case Qt::AlignVCenter:
435 default:
436 topIndent = maxSize.height() / 2 - (viewRect.top() + viewRect.bottom()) / 2;
437 break;
438 }
439
440 vbar->setRange(min: 0, max: 0);
441 } else {
442 topIndent = 0;
443
444 vbar->setRange(min: top, max: bottom);
445 vbar->setPageStep(height);
446 vbar->setSingleStep(height / 20);
447
448 if (oldTopIndent != 0)
449 vbar->setValue(-oldTopIndent);
450 }
451
452 // Restorethe center point from before the ranges changed.
453 lastCenterPoint = savedLastCenterPoint;
454
455 // Issue a full update if the indents change.
456 // ### If the transform is still the same, we can get away with just a
457 // scroll instead.
458 if (oldLeftIndent != leftIndent || oldTopIndent != topIndent) {
459 dirtyScroll = true;
460 updateAll();
461 } else if (q->isRightToLeft() && !leftIndent) {
462 // In reverse mode, the horizontal scroll always changes after the content
463 // size has changed, as the scroll is calculated by summing the min and
464 // max values of the range and subtracting the current value. In normal
465 // mode the scroll remains unchanged unless the indent has changed.
466 dirtyScroll = true;
467 }
468
469 if (cacheMode & QGraphicsView::CacheBackground) {
470 // Invalidate the background pixmap
471 mustResizeBackgroundPixmap = true;
472 }
473}
474
475/*!
476 \internal
477*/
478void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor)
479{
480 Q_Q(QGraphicsView);
481 switch (anchor) {
482 case QGraphicsView::AnchorUnderMouse: {
483 if (q->underMouse()) {
484 // Last scene pos: lastMouseMoveScenePoint
485 // Current mouse pos:
486 QPointF transformationDiff = mapToScene(point: viewport->rect().toRectF().center())
487 - mapToScene(point: viewport->mapFromGlobal(QCursor::pos().toPointF()));
488 q->centerOn(pos: lastMouseMoveScenePoint + transformationDiff);
489 } else {
490 q->centerOn(pos: lastCenterPoint);
491 }
492 break;
493 }
494 case QGraphicsView::AnchorViewCenter:
495 q->centerOn(pos: lastCenterPoint);
496 break;
497 case QGraphicsView::NoAnchor:
498 break;
499 }
500}
501
502/*!
503 \internal
504*/
505void QGraphicsViewPrivate::updateLastCenterPoint()
506{
507 lastCenterPoint = mapToScene(point: viewport->rect().toRectF().center());
508}
509
510/*!
511 \internal
512
513 Returns the horizontal scroll value (the X value of the left edge of the
514 viewport).
515*/
516qint64 QGraphicsViewPrivate::horizontalScroll() const
517{
518 if (dirtyScroll)
519 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
520 return scrollX;
521}
522
523/*!
524 \internal
525
526 Returns the vertical scroll value (the X value of the top edge of the
527 viewport).
528*/
529qint64 QGraphicsViewPrivate::verticalScroll() const
530{
531 if (dirtyScroll)
532 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
533 return scrollY;
534}
535
536/*!
537 \internal
538
539 Maps the given rectangle to the scene using QTransform::mapRect()
540*/
541QRectF QGraphicsViewPrivate::mapRectToScene(const QRect &rect) const
542{
543 if (dirtyScroll)
544 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
545 QRectF scrolled = QRectF(rect.translated(dx: scrollX, dy: scrollY));
546 return identityMatrix ? scrolled : matrix.inverted().mapRect(scrolled);
547}
548
549
550/*!
551 \internal
552
553 Maps the given rectangle from the scene using QTransform::mapRect()
554*/
555QRectF QGraphicsViewPrivate::mapRectFromScene(const QRectF &rect) const
556{
557 if (dirtyScroll)
558 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
559 return (identityMatrix ? rect : matrix.mapRect(rect)).translated(dx: -scrollX, dy: -scrollY);
560}
561
562/*!
563 \internal
564*/
565void QGraphicsViewPrivate::updateScroll()
566{
567 Q_Q(QGraphicsView);
568 scrollX = qint64(-leftIndent);
569 if (q->isRightToLeft()) {
570 if (!leftIndent) {
571 scrollX += hbar->minimum();
572 scrollX += hbar->maximum();
573 scrollX -= hbar->value();
574 }
575 } else {
576 scrollX += hbar->value();
577 }
578
579 scrollY = qint64(vbar->value() - topIndent);
580
581 dirtyScroll = false;
582}
583
584/*!
585 \internal
586
587 * don't start scrolling when a drag mode has been set
588 * don't start scrolling on a movable item
589*/
590bool QGraphicsViewPrivate::canStartScrollingAt(const QPoint &startPos) const
591{
592 Q_Q(const QGraphicsView);
593 if (q->dragMode() != QGraphicsView::NoDrag)
594 return false;
595
596 const QGraphicsItem *childItem = q->itemAt(pos: startPos);
597
598 if (!startPos.isNull() && childItem && (childItem->flags() & QGraphicsItem::ItemIsMovable))
599 return false;
600
601 return QAbstractScrollAreaPrivate::canStartScrollingAt(startPos);
602}
603
604/*!
605 \internal
606*/
607void QGraphicsViewPrivate::replayLastMouseEvent()
608{
609 if (!useLastMouseEvent || !scene)
610 return;
611 QSinglePointEvent *spe = static_cast<QSinglePointEvent *>(&lastMouseEvent);
612 mouseMoveEventHandler(event: static_cast<QMouseEvent *>(spe));
613}
614
615/*!
616 \internal
617*/
618void QGraphicsViewPrivate::storeMouseEvent(QMouseEvent *event)
619{
620 useLastMouseEvent = true;
621 lastMouseEvent = *event;
622}
623
624void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event)
625{
626 Q_Q(QGraphicsView);
627
628#if QT_CONFIG(rubberband)
629 updateRubberBand(event);
630#endif
631
632 storeMouseEvent(event);
633 lastMouseEvent.setAccepted(false);
634
635 if (!sceneInteractionAllowed)
636 return;
637 if (handScrolling)
638 return;
639 if (!scene)
640 return;
641
642 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
643 mouseEvent.setWidget(viewport);
644 mouseEvent.setButtonDownScenePos(button: mousePressButton, pos: mousePressScenePoint);
645 mouseEvent.setButtonDownScreenPos(button: mousePressButton, pos: mousePressScreenPoint);
646 mouseEvent.setScenePos(q->mapToScene(point: event->position().toPoint()));
647 mouseEvent.setScreenPos(event->globalPosition().toPoint());
648 mouseEvent.setLastScenePos(lastMouseMoveScenePoint);
649 mouseEvent.setLastScreenPos(lastMouseMoveScreenPoint);
650 mouseEvent.setButtons(event->buttons());
651 mouseEvent.setButton(event->button());
652 mouseEvent.setModifiers(event->modifiers());
653 mouseEvent.setSource(event->source());
654 mouseEvent.setFlags(event->flags());
655 mouseEvent.setTimestamp(event->timestamp());
656 lastMouseMoveScenePoint = mouseEvent.scenePos();
657 lastMouseMoveScreenPoint = mouseEvent.screenPos();
658 mouseEvent.setAccepted(false);
659 if (event->spontaneous())
660 qt_sendSpontaneousEvent(receiver: scene, event: &mouseEvent);
661 else
662 QCoreApplication::sendEvent(receiver: scene, event: &mouseEvent);
663
664 // Remember whether the last event was accepted or not.
665 lastMouseEvent.setAccepted(mouseEvent.isAccepted());
666
667 if (mouseEvent.isAccepted() && mouseEvent.buttons() != 0) {
668 // The event was delivered to a mouse grabber; the press is likely to
669 // have set a cursor, and we must not change it.
670 return;
671 }
672
673#ifndef QT_NO_CURSOR
674 // If all the items ignore hover events, we don't look-up any items
675 // in QGraphicsScenePrivate::dispatchHoverEvent, hence the
676 // cachedItemsUnderMouse list will be empty. We therefore do the look-up
677 // for cursor items here if not all items use the default cursor.
678 if (scene->d_func()->allItemsIgnoreHoverEvents && !scene->d_func()->allItemsUseDefaultCursor
679 && scene->d_func()->cachedItemsUnderMouse.isEmpty()) {
680 scene->d_func()->cachedItemsUnderMouse = scene->d_func()->itemsAtPosition(screenPos: mouseEvent.screenPos(),
681 scenePos: mouseEvent.scenePos(),
682 widget: mouseEvent.widget());
683 }
684 // Find the topmost item under the mouse with a cursor.
685 for (QGraphicsItem *item : std::as_const(t&: scene->d_func()->cachedItemsUnderMouse)) {
686 if (item->isEnabled() && item->hasCursor()) {
687 _q_setViewportCursor(cursor: item->cursor());
688 return;
689 }
690 }
691
692 // No items with cursors found; revert to the view cursor.
693 if (hasStoredOriginalCursor) {
694 // Restore the original viewport cursor.
695 hasStoredOriginalCursor = false;
696 viewport->setCursor(originalCursor);
697 }
698#endif
699}
700
701/*!
702 \internal
703*/
704#if QT_CONFIG(rubberband)
705QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRect &rect) const
706{
707 QStyleHintReturnMask mask;
708 QStyleOptionRubberBand option;
709 option.initFrom(w: widget);
710 option.rect = rect;
711 option.opaque = false;
712 option.shape = QRubberBand::Rectangle;
713
714 QRegion tmp;
715 tmp += rect.adjusted(xp1: -1, yp1: -1, xp2: 1, yp2: 1);
716 if (widget->style()->styleHint(stylehint: QStyle::SH_RubberBand_Mask, opt: &option, widget, returnData: &mask))
717 tmp &= mask.region;
718 return tmp;
719}
720
721void QGraphicsViewPrivate::updateRubberBand(const QMouseEvent *event)
722{
723 Q_Q(QGraphicsView);
724 if (dragMode != QGraphicsView::RubberBandDrag || !sceneInteractionAllowed || !rubberBanding)
725 return;
726 // Check for enough drag distance
727 if ((mousePressViewPoint - event->position().toPoint()).manhattanLength() < QApplication::startDragDistance())
728 return;
729
730 // Update old rubberband
731 if (viewportUpdateMode != QGraphicsView::NoViewportUpdate && !rubberBandRect.isEmpty()) {
732 if (viewportUpdateMode != QGraphicsView::FullViewportUpdate)
733 q->viewport()->update(rubberBandRegion(widget: q->viewport(), rect: rubberBandRect));
734 else
735 updateAll();
736 }
737
738 // Stop rubber banding if the user has let go of all buttons (even
739 // if we didn't get the release events).
740 if (!event->buttons()) {
741 rubberBanding = false;
742 rubberBandSelectionOperation = Qt::ReplaceSelection;
743 if (!rubberBandRect.isNull()) {
744 rubberBandRect = QRect();
745 emit q->rubberBandChanged(viewportRect: rubberBandRect, fromScenePoint: QPointF(), toScenePoint: QPointF());
746 }
747 return;
748 }
749
750 QRect oldRubberband = rubberBandRect;
751
752 // Update rubberband position
753 const QPoint mp = q->mapFromScene(point: mousePressScenePoint);
754 const QPoint ep = event->position().toPoint();
755 rubberBandRect = QRect(qMin(a: mp.x(), b: ep.x()), qMin(a: mp.y(), b: ep.y()),
756 qAbs(t: mp.x() - ep.x()) + 1, qAbs(t: mp.y() - ep.y()) + 1);
757
758 if (rubberBandRect != oldRubberband || lastRubberbandScenePoint != lastMouseMoveScenePoint) {
759 lastRubberbandScenePoint = lastMouseMoveScenePoint;
760 oldRubberband = rubberBandRect;
761 emit q->rubberBandChanged(viewportRect: rubberBandRect, fromScenePoint: mousePressScenePoint, toScenePoint: lastRubberbandScenePoint);
762 }
763
764 // Update new rubberband
765 if (viewportUpdateMode != QGraphicsView::NoViewportUpdate) {
766 if (viewportUpdateMode != QGraphicsView::FullViewportUpdate)
767 q->viewport()->update(rubberBandRegion(widget: q->viewport(), rect: rubberBandRect));
768 else
769 updateAll();
770 }
771 // Set the new selection area
772 QPainterPath selectionArea;
773 selectionArea.addPolygon(polygon: q->mapToScene(rect: rubberBandRect));
774 selectionArea.closeSubpath();
775 if (scene)
776 scene->setSelectionArea(path: selectionArea, selectionOperation: rubberBandSelectionOperation, mode: rubberBandSelectionMode, deviceTransform: q->viewportTransform());
777}
778
779void QGraphicsViewPrivate::clearRubberBand()
780{
781 Q_Q(QGraphicsView);
782 if (dragMode != QGraphicsView::RubberBandDrag || !sceneInteractionAllowed || !rubberBanding)
783 return;
784
785 if (viewportUpdateMode != QGraphicsView::NoViewportUpdate) {
786 if (viewportUpdateMode != QGraphicsView::FullViewportUpdate)
787 q->viewport()->update(rubberBandRegion(widget: q->viewport(), rect: rubberBandRect));
788 else
789 updateAll();
790 }
791
792 rubberBanding = false;
793 rubberBandSelectionOperation = Qt::ReplaceSelection;
794 if (!rubberBandRect.isNull()) {
795 rubberBandRect = QRect();
796 emit q->rubberBandChanged(viewportRect: rubberBandRect, fromScenePoint: QPointF(), toScenePoint: QPointF());
797 }
798}
799#endif
800
801/*!
802 \internal
803*/
804#ifndef QT_NO_CURSOR
805void QGraphicsViewPrivate::_q_setViewportCursor(const QCursor &cursor)
806{
807 if (!hasStoredOriginalCursor) {
808 hasStoredOriginalCursor = true;
809 originalCursor = viewport->cursor();
810 }
811 viewport->setCursor(cursor);
812}
813#endif
814
815/*!
816 \internal
817*/
818#ifndef QT_NO_CURSOR
819void QGraphicsViewPrivate::_q_unsetViewportCursor()
820{
821 Q_Q(QGraphicsView);
822 const auto items = q->items(pos: lastMouseEvent.position().toPoint());
823 for (QGraphicsItem *item : items) {
824 if (item->isEnabled() && item->hasCursor()) {
825 _q_setViewportCursor(cursor: item->cursor());
826 return;
827 }
828 }
829
830 // Restore the original viewport cursor.
831 if (hasStoredOriginalCursor) {
832 hasStoredOriginalCursor = false;
833 if (dragMode == QGraphicsView::ScrollHandDrag)
834 viewport->setCursor(Qt::OpenHandCursor);
835 else
836 viewport->setCursor(originalCursor);
837 }
838}
839#endif
840
841/*!
842 \internal
843*/
844void QGraphicsViewPrivate::storeDragDropEvent(const QGraphicsSceneDragDropEvent *event)
845{
846 delete lastDragDropEvent;
847 lastDragDropEvent = new QGraphicsSceneDragDropEvent(event->type());
848 lastDragDropEvent->setScenePos(event->scenePos());
849 lastDragDropEvent->setScreenPos(event->screenPos());
850 lastDragDropEvent->setButtons(event->buttons());
851 lastDragDropEvent->setModifiers(event->modifiers());
852 lastDragDropEvent->setPossibleActions(event->possibleActions());
853 lastDragDropEvent->setProposedAction(event->proposedAction());
854 lastDragDropEvent->setDropAction(event->dropAction());
855 lastDragDropEvent->setMimeData(event->mimeData());
856 lastDragDropEvent->setWidget(event->widget());
857 lastDragDropEvent->setSource(event->source());
858 lastDragDropEvent->setTimestamp(event->timestamp());
859}
860
861/*!
862 \internal
863*/
864void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
865 QDropEvent *source)
866{
867#if QT_CONFIG(draganddrop)
868 Q_Q(QGraphicsView);
869 dest->setScenePos(q->mapToScene(point: source->position().toPoint()));
870 dest->setScreenPos(q->mapToGlobal(source->position().toPoint()));
871 dest->setButtons(source->buttons());
872 dest->setModifiers(source->modifiers());
873 dest->setPossibleActions(source->possibleActions());
874 dest->setProposedAction(source->proposedAction());
875 dest->setDropAction(source->dropAction());
876 dest->setMimeData(source->mimeData());
877 dest->setWidget(viewport);
878 dest->setSource(qobject_cast<QWidget *>(o: source->source()));
879#else
880 Q_UNUSED(dest);
881 Q_UNUSED(source);
882#endif
883}
884
885/*!
886 \internal
887*/
888QTransform QGraphicsViewPrivate::mapToViewTransform(const QGraphicsItem *item) const
889{
890 Q_Q(const QGraphicsView);
891 if (dirtyScroll)
892 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
893
894 if (item->d_ptr->itemIsUntransformable())
895 return item->deviceTransform(viewportTransform: q->viewportTransform());
896
897 // Translate-only
898 // COMBINE
899 QPointF offset;
900 const QGraphicsItem *parentItem = item;
901 const QGraphicsItemPrivate *itemd;
902 do {
903 itemd = parentItem->d_ptr.data();
904 if (itemd->transformData)
905 break;
906 offset += itemd->pos;
907 } while ((parentItem = itemd->parent));
908
909 QTransform move = QTransform::fromTranslate(dx: offset.x(), dy: offset.y());
910 if (!parentItem) {
911 move.translate(dx: -scrollX, dy: -scrollY);
912 return identityMatrix ? move : matrix * move;
913 }
914 QTransform tr = parentItem->sceneTransform();
915 if (!identityMatrix)
916 tr *= matrix;
917 return move * tr * QTransform::fromTranslate(dx: -scrollX, dy: -scrollY);
918}
919
920QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const
921{
922 return mapToViewTransform(item).mapRect(rect).toAlignedRect();
923}
924
925/*!
926 \internal
927*/
928QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const
929{
930 Q_Q(const QGraphicsView);
931 if (dirtyScroll)
932 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
933
934 // Accurate bounding region
935 QTransform itv = item->deviceTransform(viewportTransform: q->viewportTransform());
936 return item->boundingRegion(itemToDeviceTransform: itv) & itv.mapRect(rect).toAlignedRect();
937}
938
939/*!
940 \internal
941*/
942void QGraphicsViewPrivate::processPendingUpdates()
943{
944 if (!scene)
945 return;
946
947 if (fullUpdatePending) {
948 viewport->update();
949 } else if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) {
950 viewport->update(dirtyBoundingRect);
951 } else {
952 viewport->update(dirtyRegion); // Already adjusted in updateRect/Region.
953 }
954
955 dirtyBoundingRect = QRect();
956 dirtyRegion = QRegion();
957}
958
959static inline bool intersectsViewport(const QRect &r, int width, int height)
960{ return !(r.left() > width) && !(r.right() < 0) && !(r.top() >= height) && !(r.bottom() < 0); }
961
962static inline bool containsViewport(const QRect &r, int width, int height)
963{ return r.left() <= 0 && r.top() <= 0 && r.right() >= width - 1 && r.bottom() >= height - 1; }
964
965static inline void QRect_unite(QRect *rect, const QRect &other)
966{
967 if (rect->isEmpty()) {
968 *rect = other;
969 } else {
970 rect->setCoords(xp1: qMin(a: rect->left(), b: other.left()), yp1: qMin(a: rect->top(), b: other.top()),
971 xp2: qMax(a: rect->right(), b: other.right()), yp2: qMax(a: rect->bottom(), b: other.bottom()));
972 }
973}
974
975/*
976 Calling this function results in update rects being clipped to the item's
977 bounding rect. Note that updates prior to this function call is not clipped.
978 The clip is removed by passing \nullptr.
979*/
980void QGraphicsViewPrivate::setUpdateClip(QGraphicsItem *item)
981{
982 Q_Q(QGraphicsView);
983 // We simply ignore the request if the update mode is either FullViewportUpdate
984 // or NoViewportUpdate; in that case there's no point in clipping anything.
985 if (!item || viewportUpdateMode == QGraphicsView::NoViewportUpdate
986 || viewportUpdateMode == QGraphicsView::FullViewportUpdate) {
987 hasUpdateClip = false;
988 return;
989 }
990
991 // Calculate the clip (item's bounding rect in view coordinates).
992 // Optimized version of:
993 // QRect clip = item->deviceTransform(q->viewportTransform())
994 // .mapRect(item->boundingRect()).toAlignedRect();
995 QRect clip;
996 if (item->d_ptr->itemIsUntransformable()) {
997 QTransform xform = item->deviceTransform(viewportTransform: q->viewportTransform());
998 clip = xform.mapRect(item->boundingRect()).toAlignedRect();
999 } else if (item->d_ptr->sceneTransformTranslateOnly && identityMatrix) {
1000 QRectF r(item->boundingRect());
1001 r.translate(dx: item->d_ptr->sceneTransform.dx() - horizontalScroll(),
1002 dy: item->d_ptr->sceneTransform.dy() - verticalScroll());
1003 clip = r.toAlignedRect();
1004 } else if (!q->isTransformed()) {
1005 clip = item->d_ptr->sceneTransform.mapRect(item->boundingRect()).toAlignedRect();
1006 } else {
1007 QTransform xform = item->d_ptr->sceneTransform;
1008 xform *= q->viewportTransform();
1009 clip = xform.mapRect(item->boundingRect()).toAlignedRect();
1010 }
1011
1012 if (hasUpdateClip) {
1013 // Intersect with old clip.
1014 updateClip &= clip;
1015 } else {
1016 updateClip = clip;
1017 hasUpdateClip = true;
1018 }
1019}
1020
1021bool QGraphicsViewPrivate::updateRegion(const QRectF &rect, const QTransform &xform)
1022{
1023 if (rect.isEmpty())
1024 return false;
1025
1026 if (viewportUpdateMode != QGraphicsView::MinimalViewportUpdate
1027 && viewportUpdateMode != QGraphicsView::SmartViewportUpdate) {
1028 // No point in updating with QRegion granularity; use the rect instead.
1029 return updateRectF(rect: xform.mapRect(rect));
1030 }
1031
1032 // Update mode is either Minimal or Smart, so we have to do a potentially slow operation,
1033 // which is clearly documented here: QGraphicsItem::setBoundingRegionGranularity.
1034 const QRegion region = xform.map(r: QRegion(rect.toAlignedRect()));
1035 QRect viewRect = region.boundingRect();
1036 const bool dontAdjustForAntialiasing = optimizationFlags & QGraphicsView::DontAdjustForAntialiasing;
1037 if (dontAdjustForAntialiasing)
1038 viewRect.adjust(dx1: -1, dy1: -1, dx2: 1, dy2: 1);
1039 else
1040 viewRect.adjust(dx1: -2, dy1: -2, dx2: 2, dy2: 2);
1041 if (!intersectsViewport(r: viewRect, width: viewport->width(), height: viewport->height()))
1042 return false; // Update region for sure outside viewport.
1043
1044 for (QRect viewRect : region) {
1045 if (dontAdjustForAntialiasing)
1046 viewRect.adjust(dx1: -1, dy1: -1, dx2: 1, dy2: 1);
1047 else
1048 viewRect.adjust(dx1: -2, dy1: -2, dx2: 2, dy2: 2);
1049 if (hasUpdateClip)
1050 viewRect &= updateClip;
1051 dirtyRegion += viewRect;
1052 }
1053
1054 return true;
1055}
1056
1057// NB! Assumes the rect 'r' is already aligned and adjusted for antialiasing.
1058// For QRectF use updateRectF(const QRectF &) to ensure proper adjustments.
1059bool QGraphicsViewPrivate::updateRect(const QRect &r)
1060{
1061 if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate
1062 || !intersectsViewport(r, width: viewport->width(), height: viewport->height())) {
1063 return false;
1064 }
1065
1066 switch (viewportUpdateMode) {
1067 case QGraphicsView::FullViewportUpdate:
1068 fullUpdatePending = true;
1069 viewport->update();
1070 break;
1071 case QGraphicsView::BoundingRectViewportUpdate:
1072 if (hasUpdateClip)
1073 QRect_unite(rect: &dirtyBoundingRect, other: r & updateClip);
1074 else
1075 QRect_unite(rect: &dirtyBoundingRect, other: r);
1076 if (containsViewport(r: dirtyBoundingRect, width: viewport->width(), height: viewport->height())) {
1077 fullUpdatePending = true;
1078 viewport->update();
1079 }
1080 break;
1081 case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE
1082 case QGraphicsView::MinimalViewportUpdate:
1083 if (hasUpdateClip)
1084 dirtyRegion += r & updateClip;
1085 else
1086 dirtyRegion += r;
1087 break;
1088 default:
1089 break;
1090 }
1091
1092 return true;
1093}
1094
1095QStyleOptionGraphicsItem *QGraphicsViewPrivate::allocStyleOptionsArray(int numItems)
1096{
1097 if (mustAllocateStyleOptions || (numItems > styleOptions.capacity()))
1098 // too many items, let's allocate on-the-fly
1099 return new QStyleOptionGraphicsItem[numItems];
1100
1101 // expand only whenever necessary
1102 if (numItems > styleOptions.size())
1103 styleOptions.resize(size: numItems);
1104
1105 mustAllocateStyleOptions = true;
1106 return styleOptions.data();
1107}
1108
1109void QGraphicsViewPrivate::freeStyleOptionsArray(QStyleOptionGraphicsItem *array)
1110{
1111 mustAllocateStyleOptions = false;
1112 if (array != styleOptions.data())
1113 delete [] array;
1114}
1115
1116extern QPainterPath qt_regionToPath(const QRegion &region);
1117
1118/*!
1119 ### Adjustments in findItems: mapToScene(QRect) forces us to adjust the
1120 input rectangle by (0, 0, 1, 1), because it uses QRect::bottomRight()
1121 (etc) when mapping the rectangle to a polygon (which is _wrong_). In
1122 addition, as QGraphicsItem::boundingRect() is defined in logical space,
1123 but the default pen for QPainter is cosmetic with a width of 0, QPainter
1124 is at risk of painting 1 pixel outside the bounding rect. Therefore we
1125 must search for items with an adjustment of (-1, -1, 1, 1).
1126*/
1127QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems,
1128 const QTransform &viewTransform) const
1129{
1130 Q_Q(const QGraphicsView);
1131
1132 // Step 1) If all items are contained within the expose region, then
1133 // return a list of all visible items. ### the scene's growing bounding
1134 // rect does not take into account untransformable items.
1135 const QRectF exposedRegionSceneBounds = q->mapToScene(rect: exposedRegion.boundingRect().adjusted(xp1: -1, yp1: -1, xp2: 1, yp2: 1))
1136 .boundingRect();
1137 if (exposedRegionSceneBounds.contains(r: scene->sceneRect())) {
1138 Q_ASSERT(allItems);
1139 *allItems = true;
1140
1141 // All items are guaranteed within the exposed region.
1142 return scene->items(order: Qt::AscendingOrder);
1143 }
1144
1145 // Step 2) If the expose region is a simple rect and the view is only
1146 // translated or scaled, search for items using
1147 // QGraphicsScene::items(QRectF).
1148 bool simpleRectLookup = exposedRegion.rectCount() == 1 && matrix.type() <= QTransform::TxScale;
1149 if (simpleRectLookup) {
1150 return scene->items(rect: exposedRegionSceneBounds,
1151 mode: Qt::IntersectsItemBoundingRect,
1152 order: Qt::AscendingOrder, deviceTransform: viewTransform);
1153 }
1154
1155 // If the region is complex or the view has a complex transform, adjust
1156 // the expose region, convert it to a path, and then search for items
1157 // using QGraphicsScene::items(QPainterPath);
1158 QRegion adjustedRegion;
1159 for (const QRect &r : exposedRegion)
1160 adjustedRegion += r.adjusted(xp1: -1, yp1: -1, xp2: 1, yp2: 1);
1161
1162 const QPainterPath exposedScenePath(q->mapToScene(path: qt_regionToPath(region: adjustedRegion)));
1163 return scene->items(path: exposedScenePath, mode: Qt::IntersectsItemBoundingRect,
1164 order: Qt::AscendingOrder, deviceTransform: viewTransform);
1165}
1166
1167/*!
1168 \internal
1169
1170 Enables input methods for the view if and only if the current focus item of
1171 the scene accepts input methods. Call function whenever that condition has
1172 potentially changed.
1173*/
1174void QGraphicsViewPrivate::updateInputMethodSensitivity()
1175{
1176 Q_Q(QGraphicsView);
1177 QGraphicsItem *focusItem = nullptr;
1178 bool enabled = scene && (focusItem = scene->focusItem())
1179 && (focusItem->d_ptr->flags & QGraphicsItem::ItemAcceptsInputMethod);
1180 q->setAttribute(Qt::WA_InputMethodEnabled, on: enabled);
1181 q->viewport()->setAttribute(Qt::WA_InputMethodEnabled, on: enabled);
1182
1183 if (!enabled) {
1184 q->setInputMethodHints({ });
1185 return;
1186 }
1187
1188 QGraphicsProxyWidget *proxy = focusItem->d_ptr->isWidget && focusItem->d_ptr->isProxyWidget()
1189 ? static_cast<QGraphicsProxyWidget *>(focusItem) : nullptr;
1190 if (!proxy) {
1191 q->setInputMethodHints(focusItem->inputMethodHints());
1192 } else if (QWidget *widget = proxy->widget()) {
1193 if (QWidget *fw = widget->focusWidget())
1194 widget = fw;
1195 q->setInputMethodHints(widget->inputMethodHints());
1196 } else {
1197 q->setInputMethodHints({ });
1198 }
1199}
1200
1201/*!
1202 Constructs a QGraphicsView. \a parent is passed to QWidget's constructor.
1203*/
1204QGraphicsView::QGraphicsView(QWidget *parent)
1205 : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
1206{
1207 setViewport(nullptr);
1208 setAcceptDrops(true);
1209 setBackgroundRole(QPalette::Base);
1210 // Investigate leaving these disabled by default.
1211 setAttribute(Qt::WA_InputMethodEnabled);
1212 viewport()->setAttribute(Qt::WA_InputMethodEnabled);
1213}
1214
1215/*!
1216 Constructs a QGraphicsView and sets the visualized scene to \a
1217 scene. \a parent is passed to QWidget's constructor.
1218*/
1219QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent)
1220 : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
1221{
1222 setScene(scene);
1223 setViewport(nullptr);
1224 setAcceptDrops(true);
1225 setBackgroundRole(QPalette::Base);
1226 // Investigate leaving these disabled by default.
1227 setAttribute(Qt::WA_InputMethodEnabled);
1228 viewport()->setAttribute(Qt::WA_InputMethodEnabled);
1229}
1230
1231/*!
1232 \internal
1233 */
1234QGraphicsView::QGraphicsView(QGraphicsViewPrivate &dd, QWidget *parent)
1235 : QAbstractScrollArea(dd, parent)
1236{
1237 setViewport(nullptr);
1238 setAcceptDrops(true);
1239 setBackgroundRole(QPalette::Base);
1240 // Investigate leaving these disabled by default.
1241 setAttribute(Qt::WA_InputMethodEnabled);
1242 viewport()->setAttribute(Qt::WA_InputMethodEnabled);
1243}
1244
1245/*!
1246 Destructs the QGraphicsView object.
1247*/
1248QGraphicsView::~QGraphicsView()
1249{
1250 Q_D(QGraphicsView);
1251 if (d->scene)
1252 d->scene->d_func()->views.removeAll(t: this);
1253 delete d->lastDragDropEvent;
1254}
1255
1256/*!
1257 \reimp
1258*/
1259QSize QGraphicsView::sizeHint() const
1260{
1261 Q_D(const QGraphicsView);
1262 if (d->scene) {
1263 QSizeF baseSize = d->matrix.mapRect(sceneRect()).size();
1264 baseSize += QSizeF(d->frameWidth * 2, d->frameWidth * 2);
1265 return baseSize.boundedTo(otherSize: (3 * QGuiApplication::primaryScreen()->virtualSize()) / 4).toSize();
1266 }
1267 return QAbstractScrollArea::sizeHint();
1268}
1269
1270/*!
1271 \property QGraphicsView::renderHints
1272 \brief the default render hints for the view
1273
1274 These hints are
1275 used to initialize QPainter before each visible item is drawn. QPainter
1276 uses render hints to toggle rendering features such as antialiasing and
1277 smooth pixmap transformation.
1278
1279 QPainter::TextAntialiasing is enabled by default.
1280
1281 Example:
1282
1283 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 1
1284*/
1285QPainter::RenderHints QGraphicsView::renderHints() const
1286{
1287 Q_D(const QGraphicsView);
1288 return d->renderHints;
1289}
1290void QGraphicsView::setRenderHints(QPainter::RenderHints hints)
1291{
1292 Q_D(QGraphicsView);
1293 if (hints == d->renderHints)
1294 return;
1295 d->renderHints = hints;
1296 d->updateAll();
1297}
1298
1299/*!
1300 If \a enabled is true, the render hint \a hint is enabled; otherwise it
1301 is disabled.
1302
1303 \sa renderHints
1304*/
1305void QGraphicsView::setRenderHint(QPainter::RenderHint hint, bool enabled)
1306{
1307 Q_D(QGraphicsView);
1308 QPainter::RenderHints oldHints = d->renderHints;
1309 d->renderHints.setFlag(flag: hint, on: enabled);
1310 if (oldHints != d->renderHints)
1311 d->updateAll();
1312}
1313
1314/*!
1315 \property QGraphicsView::alignment
1316 \brief the alignment of the scene in the view when the whole
1317 scene is visible.
1318
1319 If the whole scene is visible in the view, (i.e., there are no visible
1320 scroll bars,) the view's alignment will decide where the scene will be
1321 rendered in the view. For example, if the alignment is Qt::AlignCenter,
1322 which is default, the scene will be centered in the view, and if the
1323 alignment is (Qt::AlignLeft | Qt::AlignTop), the scene will be rendered in
1324 the top-left corner of the view.
1325*/
1326Qt::Alignment QGraphicsView::alignment() const
1327{
1328 Q_D(const QGraphicsView);
1329 return d->alignment;
1330}
1331void QGraphicsView::setAlignment(Qt::Alignment alignment)
1332{
1333 Q_D(QGraphicsView);
1334 if (d->alignment != alignment) {
1335 d->alignment = alignment;
1336 d->recalculateContentSize();
1337 }
1338}
1339
1340/*!
1341 \property QGraphicsView::transformationAnchor
1342 \brief how the view should position the scene during transformations.
1343
1344 QGraphicsView uses this property to decide how to position the scene in
1345 the viewport when the transformation matrix changes, and the coordinate
1346 system of the view is transformed. The default behavior, AnchorViewCenter,
1347 ensures that the scene point at the center of the view remains unchanged
1348 during transformations (e.g., when rotating, the scene will appear to
1349 rotate around the center of the view).
1350
1351 Note that the effect of this property is noticeable when only a part of the
1352 scene is visible (i.e., when there are scroll bars). Otherwise, if the
1353 whole scene fits in the view, QGraphicsScene uses the view \l alignment to
1354 position the scene in the view.
1355
1356 \sa alignment, resizeAnchor
1357*/
1358QGraphicsView::ViewportAnchor QGraphicsView::transformationAnchor() const
1359{
1360 Q_D(const QGraphicsView);
1361 return d->transformationAnchor;
1362}
1363void QGraphicsView::setTransformationAnchor(ViewportAnchor anchor)
1364{
1365 Q_D(QGraphicsView);
1366 d->transformationAnchor = anchor;
1367
1368 // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse
1369 // in order to have up-to-date information for centering the view.
1370 if (d->transformationAnchor == AnchorUnderMouse)
1371 d->viewport->setMouseTracking(true);
1372}
1373
1374/*!
1375 \property QGraphicsView::resizeAnchor
1376 \brief how the view should position the scene when the view is resized.
1377
1378 QGraphicsView uses this property to decide how to position the scene in
1379 the viewport when the viewport widget's size changes. The default
1380 behavior, NoAnchor, leaves the scene's position unchanged during a resize;
1381 the top-left corner of the view will appear to be anchored while resizing.
1382
1383 Note that the effect of this property is noticeable when only a part of the
1384 scene is visible (i.e., when there are scroll bars). Otherwise, if the
1385 whole scene fits in the view, QGraphicsScene uses the view \l alignment to
1386 position the scene in the view.
1387
1388 \sa alignment, transformationAnchor
1389*/
1390QGraphicsView::ViewportAnchor QGraphicsView::resizeAnchor() const
1391{
1392 Q_D(const QGraphicsView);
1393 return d->resizeAnchor;
1394}
1395void QGraphicsView::setResizeAnchor(ViewportAnchor anchor)
1396{
1397 Q_D(QGraphicsView);
1398 d->resizeAnchor = anchor;
1399
1400 // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse
1401 // in order to have up-to-date information for centering the view.
1402 if (d->resizeAnchor == AnchorUnderMouse)
1403 d->viewport->setMouseTracking(true);
1404}
1405
1406/*!
1407 \property QGraphicsView::viewportUpdateMode
1408 \brief how the viewport should update its contents.
1409
1410 \since 4.3
1411
1412 QGraphicsView uses this property to decide how to update areas of the
1413 scene that have been reexposed or changed. Usually you do not need to
1414 modify this property, but there are some cases where doing so can improve
1415 rendering performance. See the ViewportUpdateMode documentation for
1416 specific details.
1417
1418 The default value is MinimalViewportUpdate, where QGraphicsView will
1419 update as small an area of the viewport as possible when the contents
1420 change.
1421
1422 \sa ViewportUpdateMode, cacheMode
1423*/
1424QGraphicsView::ViewportUpdateMode QGraphicsView::viewportUpdateMode() const
1425{
1426 Q_D(const QGraphicsView);
1427 return d->viewportUpdateMode;
1428}
1429void QGraphicsView::setViewportUpdateMode(ViewportUpdateMode mode)
1430{
1431 Q_D(QGraphicsView);
1432 d->viewportUpdateMode = mode;
1433}
1434
1435/*!
1436 \property QGraphicsView::optimizationFlags
1437 \brief flags that can be used to tune QGraphicsView's performance.
1438
1439 \since 4.3
1440
1441 QGraphicsView uses clipping, extra bounding rect adjustments, and certain
1442 other aids to improve rendering quality and performance for the common
1443 case graphics scene. However, depending on the target platform, the scene,
1444 and the viewport in use, some of these operations can degrade performance.
1445
1446 The effect varies from flag to flag; see the OptimizationFlags
1447 documentation for details.
1448
1449 By default, no optimization flags are enabled.
1450
1451 \sa setOptimizationFlag()
1452*/
1453QGraphicsView::OptimizationFlags QGraphicsView::optimizationFlags() const
1454{
1455 Q_D(const QGraphicsView);
1456 return d->optimizationFlags;
1457}
1458void QGraphicsView::setOptimizationFlags(OptimizationFlags flags)
1459{
1460 Q_D(QGraphicsView);
1461 d->optimizationFlags = flags;
1462}
1463
1464/*!
1465 Enables \a flag if \a enabled is true; otherwise disables \a flag.
1466
1467 \sa optimizationFlags
1468*/
1469void QGraphicsView::setOptimizationFlag(OptimizationFlag flag, bool enabled)
1470{
1471 Q_D(QGraphicsView);
1472 d->optimizationFlags.setFlag(flag, on: enabled);
1473}
1474
1475/*!
1476 \property QGraphicsView::dragMode
1477 \brief the behavior for dragging the mouse over the scene while
1478 the left mouse button is pressed.
1479
1480 This property defines what should happen when the user clicks on the scene
1481 background and drags the mouse (e.g., scrolling the viewport contents
1482 using a pointing hand cursor, or selecting multiple items with a rubber
1483 band). The default value, NoDrag, does nothing.
1484
1485 This behavior only affects mouse clicks that are not handled by any item.
1486 You can define a custom behavior by creating a subclass of QGraphicsView
1487 and reimplementing mouseMoveEvent().
1488*/
1489QGraphicsView::DragMode QGraphicsView::dragMode() const
1490{
1491 Q_D(const QGraphicsView);
1492 return d->dragMode;
1493}
1494void QGraphicsView::setDragMode(DragMode mode)
1495{
1496 Q_D(QGraphicsView);
1497 if (d->dragMode == mode)
1498 return;
1499
1500#if QT_CONFIG(rubberband)
1501 d->clearRubberBand();
1502#endif
1503
1504#ifndef QT_NO_CURSOR
1505 if (d->dragMode == ScrollHandDrag)
1506 viewport()->unsetCursor();
1507#endif
1508
1509 // If dragMode is unset while dragging, e.g. via a keyEvent, we
1510 // don't unset the handScrolling state. When enabling scrolling
1511 // again the mouseMoveEvent will automatically start scrolling,
1512 // without a mousePress
1513 if (d->dragMode == ScrollHandDrag && mode == NoDrag && d->handScrolling)
1514 d->handScrolling = false;
1515
1516 d->dragMode = mode;
1517
1518#ifndef QT_NO_CURSOR
1519 if (d->dragMode == ScrollHandDrag) {
1520 // Forget the stored viewport cursor when we enter scroll hand drag mode.
1521 d->hasStoredOriginalCursor = false;
1522 viewport()->setCursor(Qt::OpenHandCursor);
1523 }
1524#endif
1525}
1526
1527#if QT_CONFIG(rubberband)
1528/*!
1529 \property QGraphicsView::rubberBandSelectionMode
1530 \brief the behavior for selecting items with a rubber band selection rectangle.
1531 \since 4.3
1532
1533 This property defines how items are selected when using the RubberBandDrag
1534 drag mode.
1535
1536 The default value is Qt::IntersectsItemShape; all items whose shape
1537 intersects with or is contained by the rubber band are selected.
1538
1539 \sa dragMode, items(), rubberBandRect()
1540*/
1541Qt::ItemSelectionMode QGraphicsView::rubberBandSelectionMode() const
1542{
1543 Q_D(const QGraphicsView);
1544 return d->rubberBandSelectionMode;
1545}
1546void QGraphicsView::setRubberBandSelectionMode(Qt::ItemSelectionMode mode)
1547{
1548 Q_D(QGraphicsView);
1549 d->rubberBandSelectionMode = mode;
1550}
1551
1552/*!
1553 \since 5.1
1554 This functions returns the current rubber band area (in viewport coordinates) if the user
1555 is currently doing an itemselection with rubber band. When the user is not using the
1556 rubber band this functions returns (a null) QRectF().
1557
1558 Notice that part of this QRect can be outside the visual viewport. It can e.g
1559 contain negative values.
1560
1561 \sa rubberBandSelectionMode, rubberBandChanged()
1562*/
1563
1564QRect QGraphicsView::rubberBandRect() const
1565{
1566 Q_D(const QGraphicsView);
1567 if (d->dragMode != QGraphicsView::RubberBandDrag || !d->sceneInteractionAllowed || !d->rubberBanding)
1568 return QRect();
1569
1570 return d->rubberBandRect;
1571}
1572#endif
1573
1574/*!
1575 \property QGraphicsView::cacheMode
1576 \brief which parts of the view are cached
1577
1578 QGraphicsView can cache pre-rendered content in a QPixmap, which is then
1579 drawn onto the viewport. The purpose of such caching is to speed up the
1580 total rendering time for areas that are slow to render. Texture, gradient
1581 and alpha blended backgrounds, for example, can be notibly slow to render;
1582 especially with a transformed view. The CacheBackground flag enables
1583 caching of the view's background. For example:
1584
1585 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 2
1586
1587 The cache is invalidated every time the view is transformed. However, when
1588 scrolling, only partial invalidation is required.
1589
1590 By default, nothing is cached.
1591
1592 \sa resetCachedContent(), QPixmapCache
1593*/
1594QGraphicsView::CacheMode QGraphicsView::cacheMode() const
1595{
1596 Q_D(const QGraphicsView);
1597 return d->cacheMode;
1598}
1599void QGraphicsView::setCacheMode(CacheMode mode)
1600{
1601 Q_D(QGraphicsView);
1602 if (mode == d->cacheMode)
1603 return;
1604 d->cacheMode = mode;
1605 resetCachedContent();
1606}
1607
1608/*!
1609 Resets any cached content. Calling this function will clear
1610 QGraphicsView's cache. If the current cache mode is \l CacheNone, this
1611 function does nothing.
1612
1613 This function is called automatically for you when the backgroundBrush or
1614 QGraphicsScene::backgroundBrush properties change; you only need to call
1615 this function if you have reimplemented QGraphicsScene::drawBackground()
1616 or QGraphicsView::drawBackground() to draw a custom background, and need
1617 to trigger a full redraw.
1618
1619 \sa cacheMode()
1620*/
1621void QGraphicsView::resetCachedContent()
1622{
1623 Q_D(QGraphicsView);
1624 if (d->cacheMode == CacheNone)
1625 return;
1626
1627 if (d->cacheMode & CacheBackground) {
1628 // Background caching is enabled.
1629 d->mustResizeBackgroundPixmap = true;
1630 d->updateAll();
1631 } else if (d->mustResizeBackgroundPixmap) {
1632 // Background caching is disabled.
1633 // Cleanup, free some resources.
1634 d->mustResizeBackgroundPixmap = false;
1635 d->backgroundPixmap = QPixmap();
1636 d->backgroundPixmapExposed = QRegion();
1637 }
1638}
1639
1640/*!
1641 Invalidates and schedules a redraw of \a layers inside \a rect. \a rect is
1642 in scene coordinates. Any cached content for \a layers inside \a rect is
1643 unconditionally invalidated and redrawn.
1644
1645 You can call this function to notify QGraphicsView of changes to the
1646 background or the foreground of the scene. It is commonly used for scenes
1647 with tile-based backgrounds to notify changes when QGraphicsView has
1648 enabled background caching.
1649
1650 Note that QGraphicsView currently supports background caching only (see
1651 QGraphicsView::CacheBackground). This function is equivalent to calling update() if any
1652 layer but QGraphicsScene::BackgroundLayer is passed.
1653
1654 \sa QGraphicsScene::invalidate(), update()
1655*/
1656void QGraphicsView::invalidateScene(const QRectF &rect, QGraphicsScene::SceneLayers layers)
1657{
1658 Q_D(QGraphicsView);
1659 if ((layers & QGraphicsScene::BackgroundLayer) && !d->mustResizeBackgroundPixmap) {
1660 QRect viewRect = mapFromScene(rect).boundingRect();
1661 if (viewport()->rect().intersects(r: viewRect)) {
1662 // The updated background area is exposed; schedule this area for
1663 // redrawing.
1664 d->backgroundPixmapExposed += viewRect;
1665 if (d->scene)
1666 d->scene->update(rect);
1667 }
1668 }
1669}
1670
1671/*!
1672 \property QGraphicsView::interactive
1673 \brief whether the view allows scene interaction.
1674
1675 If enabled, this view is set to allow scene interaction. Otherwise, this
1676 view will not allow interaction, and any mouse or key events are ignored
1677 (i.e., it will act as a read-only view).
1678
1679 By default, this property is \c true.
1680*/
1681bool QGraphicsView::isInteractive() const
1682{
1683 Q_D(const QGraphicsView);
1684 return d->sceneInteractionAllowed;
1685}
1686void QGraphicsView::setInteractive(bool allowed)
1687{
1688 Q_D(QGraphicsView);
1689 d->sceneInteractionAllowed = allowed;
1690}
1691
1692/*!
1693 Returns a pointer to the scene that is currently visualized in the
1694 view. If no scene is currently visualized, \nullptr is returned.
1695
1696 \sa setScene()
1697*/
1698QGraphicsScene *QGraphicsView::scene() const
1699{
1700 Q_D(const QGraphicsView);
1701 return d->scene;
1702}
1703
1704/*!
1705 Sets the current scene to \a scene. If \a scene is already being
1706 viewed, this function does nothing.
1707
1708 When a scene is set on a view, the QGraphicsScene::changed() signal
1709 is automatically connected to this view's updateScene() slot, and the
1710 view's scroll bars are adjusted to fit the size of the scene.
1711
1712 The view does not take ownership of \a scene.
1713*/
1714void QGraphicsView::setScene(QGraphicsScene *scene)
1715{
1716 Q_D(QGraphicsView);
1717 if (d->scene == scene)
1718 return;
1719
1720 // Always update the viewport when the scene changes.
1721 d->updateAll();
1722
1723 // Remove the previously assigned scene.
1724 if (d->scene) {
1725 disconnect(sender: d->scene, SIGNAL(changed(QList<QRectF>)),
1726 receiver: this, SLOT(updateScene(QList<QRectF>)));
1727 disconnect(sender: d->scene, SIGNAL(sceneRectChanged(QRectF)),
1728 receiver: this, SLOT(updateSceneRect(QRectF)));
1729 d->scene->d_func()->removeView(view: this);
1730 d->connectedToScene = false;
1731
1732 if (isActiveWindow() && isVisible()) {
1733 QEvent windowDeactivate(QEvent::WindowDeactivate);
1734 QCoreApplication::sendEvent(receiver: d->scene, event: &windowDeactivate);
1735 }
1736 if (hasFocus())
1737 d->scene->clearFocus();
1738 }
1739
1740 // Assign the new scene and update the contents (scrollbars, etc.)).
1741 if ((d->scene = scene)) {
1742 connect(sender: d->scene, SIGNAL(sceneRectChanged(QRectF)),
1743 receiver: this, SLOT(updateSceneRect(QRectF)));
1744 d->updateSceneSlotReimplementedChecked = false;
1745 d->scene->d_func()->addView(view: this);
1746 d->recalculateContentSize();
1747 d->lastCenterPoint = sceneRect().center();
1748 d->keepLastCenterPoint = true;
1749 // We are only interested in mouse tracking if items accept
1750 // hover events or use non-default cursors.
1751 if (!d->scene->d_func()->allItemsIgnoreHoverEvents
1752 || !d->scene->d_func()->allItemsUseDefaultCursor) {
1753 d->viewport->setMouseTracking(true);
1754 }
1755
1756 // enable touch events if any items is interested in them
1757 if (!d->scene->d_func()->allItemsIgnoreTouchEvents)
1758 d->viewport->setAttribute(Qt::WA_AcceptTouchEvents);
1759
1760 if (isActiveWindow() && isVisible()) {
1761 QEvent windowActivate(QEvent::WindowActivate);
1762 QCoreApplication::sendEvent(receiver: d->scene, event: &windowActivate);
1763 }
1764 } else {
1765 d->recalculateContentSize();
1766 }
1767
1768 d->updateInputMethodSensitivity();
1769
1770 if (d->scene && hasFocus())
1771 d->scene->setFocus();
1772}
1773
1774/*!
1775 \property QGraphicsView::sceneRect
1776 \brief the area of the scene visualized by this view.
1777
1778 The scene rectangle defines the extent of the scene, and in the view's case,
1779 this means the area of the scene that you can navigate using the scroll
1780 bars.
1781
1782 If unset, or if a null QRectF is set, this property has the same value as
1783 QGraphicsScene::sceneRect, and it changes with
1784 QGraphicsScene::sceneRect. Otherwise, the view's scene rect is unaffected
1785 by the scene.
1786
1787 Note that, although the scene supports a virtually unlimited size, the
1788 range of the scroll bars will never exceed the range of an integer
1789 (INT_MIN, INT_MAX). When the scene is larger than the scroll bars' values,
1790 you can choose to use translate() to navigate the scene instead.
1791
1792 By default, this property contains a rectangle at the origin with zero
1793 width and height.
1794
1795 \sa QGraphicsScene::sceneRect
1796*/
1797QRectF QGraphicsView::sceneRect() const
1798{
1799 Q_D(const QGraphicsView);
1800 if (d->hasSceneRect)
1801 return d->sceneRect;
1802 if (d->scene)
1803 return d->scene->sceneRect();
1804 return QRectF();
1805}
1806void QGraphicsView::setSceneRect(const QRectF &rect)
1807{
1808 Q_D(QGraphicsView);
1809 d->hasSceneRect = !rect.isNull();
1810 d->sceneRect = rect;
1811 d->recalculateContentSize();
1812}
1813
1814/*!
1815 Rotates the current view transformation \a angle degrees clockwise.
1816
1817 \sa setTransform(), transform(), scale(), shear(), translate()
1818*/
1819void QGraphicsView::rotate(qreal angle)
1820{
1821 Q_D(QGraphicsView);
1822 QTransform matrix = d->matrix;
1823 matrix.rotate(a: angle);
1824 setTransform(matrix);
1825}
1826
1827/*!
1828 Scales the current view transformation by (\a sx, \a sy).
1829
1830 \sa setTransform(), transform(), rotate(), shear(), translate()
1831*/
1832void QGraphicsView::scale(qreal sx, qreal sy)
1833{
1834 Q_D(QGraphicsView);
1835 QTransform matrix = d->matrix;
1836 matrix.scale(sx, sy);
1837 setTransform(matrix);
1838}
1839
1840/*!
1841 Shears the current view transformation by (\a sh, \a sv).
1842
1843 \sa setTransform(), transform(), rotate(), scale(), translate()
1844*/
1845void QGraphicsView::shear(qreal sh, qreal sv)
1846{
1847 Q_D(QGraphicsView);
1848 QTransform matrix = d->matrix;
1849 matrix.shear(sh, sv);
1850 setTransform(matrix);
1851}
1852
1853/*!
1854 Translates the current view transformation by (\a dx, \a dy).
1855
1856 \sa setTransform(), transform(), rotate(), shear()
1857*/
1858void QGraphicsView::translate(qreal dx, qreal dy)
1859{
1860 Q_D(QGraphicsView);
1861 QTransform matrix = d->matrix;
1862 matrix.translate(dx, dy);
1863 setTransform(matrix);
1864}
1865
1866/*!
1867 Scrolls the contents of the viewport to ensure that the scene
1868 coordinate \a pos, is centered in the view.
1869
1870 Because \a pos is a floating point coordinate, and the scroll bars operate
1871 on integer coordinates, the centering is only an approximation.
1872
1873 \note If the item is close to or outside the border, it will be visible
1874 in the view, but not centered.
1875
1876 \sa ensureVisible()
1877*/
1878void QGraphicsView::centerOn(const QPointF &pos)
1879{
1880 Q_D(QGraphicsView);
1881 qreal width = viewport()->width();
1882 qreal height = viewport()->height();
1883 QPointF viewPoint = d->matrix.map(p: pos);
1884 QPointF oldCenterPoint = pos;
1885
1886 if (!d->leftIndent) {
1887 if (isRightToLeft()) {
1888 qint64 horizontal = 0;
1889 horizontal += horizontalScrollBar()->minimum();
1890 horizontal += horizontalScrollBar()->maximum();
1891 horizontal -= qRound(d: viewPoint.x() - width / 2.0);
1892 horizontalScrollBar()->setValue(horizontal);
1893 } else {
1894 horizontalScrollBar()->setValue(qRound(d: viewPoint.x() - width / 2.0));
1895 }
1896 }
1897 if (!d->topIndent)
1898 verticalScrollBar()->setValue(qRound(d: viewPoint.y() - height / 2.0));
1899 d->lastCenterPoint = oldCenterPoint;
1900}
1901
1902/*!
1903 \fn QGraphicsView::centerOn(qreal x, qreal y)
1904 \overload
1905
1906 This function is provided for convenience. It's equivalent to calling
1907 centerOn(QPointF(\a x, \a y)).
1908*/
1909
1910/*!
1911 \overload
1912
1913 Scrolls the contents of the viewport to ensure that \a item
1914 is centered in the view.
1915
1916 \sa ensureVisible()
1917*/
1918void QGraphicsView::centerOn(const QGraphicsItem *item)
1919{
1920 centerOn(pos: item->sceneBoundingRect().center());
1921}
1922
1923/*!
1924 Scrolls the contents of the viewport so that the scene rectangle \a rect
1925 is visible, with margins specified in pixels by \a xmargin and \a
1926 ymargin. If the specified rect cannot be reached, the contents are
1927 scrolled to the nearest valid position. The default value for both margins
1928 is 50 pixels.
1929
1930 \sa centerOn()
1931*/
1932void QGraphicsView::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
1933{
1934 Q_D(QGraphicsView);
1935 qreal width = viewport()->width();
1936 qreal height = viewport()->height();
1937 QRectF viewRect = d->matrix.mapRect(rect);
1938
1939 qreal left = d->horizontalScroll();
1940 qreal right = left + width;
1941 qreal top = d->verticalScroll();
1942 qreal bottom = top + height;
1943
1944 if (viewRect.left() <= left + xmargin) {
1945 // need to scroll from the left
1946 if (!d->leftIndent)
1947 horizontalScrollBar()->setValue(int(viewRect.left() - xmargin - 0.5));
1948 }
1949 if (viewRect.right() >= right - xmargin) {
1950 // need to scroll from the right
1951 if (!d->leftIndent)
1952 horizontalScrollBar()->setValue(int(viewRect.right() - width + xmargin + 0.5));
1953 }
1954 if (viewRect.top() <= top + ymargin) {
1955 // need to scroll from the top
1956 if (!d->topIndent)
1957 verticalScrollBar()->setValue(int(viewRect.top() - ymargin - 0.5));
1958 }
1959 if (viewRect.bottom() >= bottom - ymargin) {
1960 // need to scroll from the bottom
1961 if (!d->topIndent)
1962 verticalScrollBar()->setValue(int(viewRect.bottom() - height + ymargin + 0.5));
1963 }
1964}
1965
1966/*!
1967 \fn QGraphicsView::ensureVisible(qreal x, qreal y, qreal w, qreal h,
1968 int xmargin, int ymargin)
1969 \overload
1970
1971 This function is provided for convenience. It's equivalent to calling
1972 ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin).
1973*/
1974
1975/*!
1976 \overload
1977
1978 Scrolls the contents of the viewport so that the center of item \a item is
1979 visible, with margins specified in pixels by \a xmargin and \a ymargin. If
1980 the specified point cannot be reached, the contents are scrolled to the
1981 nearest valid position. The default value for both margins is 50 pixels.
1982
1983 \sa centerOn()
1984*/
1985void QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin, int ymargin)
1986{
1987 ensureVisible(rect: item->sceneBoundingRect(), xmargin, ymargin);
1988}
1989
1990/*!
1991 Scales the view matrix and scrolls the scroll bars to ensure that the
1992 scene rectangle \a rect fits inside the viewport. \a rect must be inside
1993 the scene rect; otherwise, fitInView() cannot guarantee that the whole
1994 rect is visible.
1995
1996 This function keeps the view's rotation, translation, or shear. The view
1997 is scaled according to \a aspectRatioMode. \a rect will be centered in the
1998 view if it does not fit tightly.
1999
2000 It's common to call fitInView() from inside a reimplementation of
2001 resizeEvent(), to ensure that the whole scene, or parts of the scene,
2002 scales automatically to fit the new size of the viewport as the view is
2003 resized. Note though, that calling fitInView() from inside resizeEvent()
2004 can lead to unwanted resize recursion, if the new transformation toggles
2005 the automatic state of the scrollbars. You can toggle the scrollbar
2006 policies to always on or always off to prevent this (see
2007 horizontalScrollBarPolicy() and verticalScrollBarPolicy()).
2008
2009 If \a rect is empty, or if the viewport is too small, this
2010 function will do nothing.
2011
2012 \sa setTransform(), ensureVisible(), centerOn()
2013*/
2014void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode)
2015{
2016 Q_D(QGraphicsView);
2017 if (!d->scene || rect.isNull())
2018 return;
2019
2020 // Reset the view scale to 1:1.
2021 QRectF unity = d->matrix.mapRect(QRectF(0, 0, 1, 1));
2022 if (unity.isEmpty())
2023 return;
2024 scale(sx: 1 / unity.width(), sy: 1 / unity.height());
2025
2026 // Find the ideal x / y scaling ratio to fit \a rect in the view.
2027 int margin = 2;
2028 QRectF viewRect = viewport()->rect().adjusted(xp1: margin, yp1: margin, xp2: -margin, yp2: -margin);
2029 if (viewRect.isEmpty())
2030 return;
2031 QRectF sceneRect = d->matrix.mapRect(rect);
2032 if (sceneRect.isEmpty())
2033 return;
2034 qreal xratio = viewRect.width() / sceneRect.width();
2035 qreal yratio = viewRect.height() / sceneRect.height();
2036
2037 // Respect the aspect ratio mode.
2038 switch (aspectRatioMode) {
2039 case Qt::KeepAspectRatio:
2040 xratio = yratio = qMin(a: xratio, b: yratio);
2041 break;
2042 case Qt::KeepAspectRatioByExpanding:
2043 xratio = yratio = qMax(a: xratio, b: yratio);
2044 break;
2045 case Qt::IgnoreAspectRatio:
2046 break;
2047 }
2048
2049 // Scale and center on the center of \a rect.
2050 scale(sx: xratio, sy: yratio);
2051 centerOn(pos: rect.center());
2052}
2053
2054/*!
2055 \fn void QGraphicsView::fitInView(qreal x, qreal y, qreal w, qreal h,
2056 Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
2057
2058 \overload
2059
2060 This convenience function is equivalent to calling
2061 fitInView(QRectF(\a x, \a y, \a w, \a h), \a aspectRatioMode).
2062
2063 \sa ensureVisible(), centerOn()
2064*/
2065
2066/*!
2067 \overload
2068
2069 Ensures that \a item fits tightly inside the view, scaling the view
2070 according to \a aspectRatioMode.
2071
2072 \sa ensureVisible(), centerOn()
2073*/
2074void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode)
2075{
2076 QPainterPath path = item->isClipped() ? item->clipPath() : item->shape();
2077 if (item->d_ptr->hasTranslateOnlySceneTransform()) {
2078 path.translate(dx: item->d_ptr->sceneTransform.dx(), dy: item->d_ptr->sceneTransform.dy());
2079 fitInView(rect: path.boundingRect(), aspectRatioMode);
2080 } else {
2081 fitInView(rect: item->d_ptr->sceneTransform.map(p: path).boundingRect(), aspectRatioMode);
2082 }
2083}
2084
2085/*!
2086 Renders the \a source rect, which is in view coordinates, from the scene
2087 into \a target, which is in paint device coordinates, using \a
2088 painter. This function is useful for capturing the contents of the view
2089 onto a paint device, such as a QImage (e.g., to take a screenshot), or for
2090 printing to QPrinter. For example:
2091
2092 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 4
2093
2094 If \a source is a null rect, this function will use viewport()->rect() to
2095 determine what to draw. If \a target is a null rect, the full dimensions
2096 of \a painter's paint device (e.g., for a QPrinter, the page size) will be
2097 used.
2098
2099 The source rect contents will be transformed according to \a
2100 aspectRatioMode to fit into the target rect. By default, the aspect ratio
2101 is kept, and \a source is scaled to fit in \a target.
2102
2103 \sa QGraphicsScene::render()
2104*/
2105void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source,
2106 Qt::AspectRatioMode aspectRatioMode)
2107{
2108 // ### Switch to using the recursive rendering algorithm instead.
2109
2110 Q_D(QGraphicsView);
2111 if (!d->scene || !(painter && painter->isActive()))
2112 return;
2113
2114 // Default source rect = viewport rect
2115 QRect sourceRect = source;
2116 if (source.isNull())
2117 sourceRect = viewport()->rect();
2118
2119 // Default target rect = device rect
2120 QRectF targetRect = target;
2121 if (target.isNull()) {
2122 if (painter->device()->devType() == QInternal::Picture)
2123 targetRect = sourceRect;
2124 else
2125 targetRect.setRect(ax: 0, ay: 0, aaw: painter->device()->width(), aah: painter->device()->height());
2126 }
2127
2128 // Find the ideal x / y scaling ratio to fit \a source into \a target.
2129 qreal xratio = targetRect.width() / sourceRect.width();
2130 qreal yratio = targetRect.height() / sourceRect.height();
2131
2132 // Scale according to the aspect ratio mode.
2133 switch (aspectRatioMode) {
2134 case Qt::KeepAspectRatio:
2135 xratio = yratio = qMin(a: xratio, b: yratio);
2136 break;
2137 case Qt::KeepAspectRatioByExpanding:
2138 xratio = yratio = qMax(a: xratio, b: yratio);
2139 break;
2140 case Qt::IgnoreAspectRatio:
2141 break;
2142 }
2143
2144 // Find all items to draw, and reverse the list (we want to draw
2145 // in reverse order).
2146 QPolygonF sourceScenePoly = mapToScene(rect: sourceRect.adjusted(xp1: -1, yp1: -1, xp2: 1, yp2: 1));
2147 QList<QGraphicsItem *> itemList = d->scene->items(polygon: sourceScenePoly,
2148 mode: Qt::IntersectsItemBoundingRect);
2149 QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
2150 int numItems = itemList.size();
2151 for (int i = 0; i < numItems; ++i)
2152 itemArray[numItems - i - 1] = itemList.at(i);
2153 itemList.clear();
2154
2155 // Setup painter matrix.
2156 QTransform moveMatrix = QTransform::fromTranslate(dx: -d->horizontalScroll(), dy: -d->verticalScroll());
2157 QTransform painterMatrix = d->matrix * moveMatrix;
2158 painterMatrix *= QTransform()
2159 .translate(dx: targetRect.left(), dy: targetRect.top())
2160 .scale(sx: xratio, sy: yratio)
2161 .translate(dx: -sourceRect.left(), dy: -sourceRect.top());
2162
2163 // Generate the style options
2164 QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
2165 for (int i = 0; i < numItems; ++i)
2166 itemArray[i]->d_ptr->initStyleOption(option: &styleOptionArray[i], worldTransform: painterMatrix, exposedRegion: targetRect.toRect());
2167
2168 painter->save();
2169
2170 // Clip in device coordinates to avoid QRegion transformations.
2171 painter->setClipRect(targetRect);
2172 QPainterPath path;
2173 path.addPolygon(polygon: sourceScenePoly);
2174 path.closeSubpath();
2175 painter->setClipPath(path: painterMatrix.map(p: path), op: Qt::IntersectClip);
2176
2177 // Transform the painter.
2178 painter->setTransform(transform: painterMatrix, combine: true);
2179
2180 // Render the scene.
2181 QRectF sourceSceneRect = sourceScenePoly.boundingRect();
2182 drawBackground(painter, rect: sourceSceneRect);
2183 drawItems(painter, numItems, items: itemArray, options: styleOptionArray);
2184 drawForeground(painter, rect: sourceSceneRect);
2185
2186 delete [] itemArray;
2187 d->freeStyleOptionsArray(array: styleOptionArray);
2188
2189 painter->restore();
2190}
2191
2192/*!
2193 Returns a list of all the items in the associated scene, in descending
2194 stacking order (i.e., the first item in the returned list is the uppermost
2195 item).
2196
2197 \sa QGraphicsScene::items(), {QGraphicsItem#Sorting}{Sorting}
2198*/
2199QList<QGraphicsItem *> QGraphicsView::items() const
2200{
2201 Q_D(const QGraphicsView);
2202 if (!d->scene)
2203 return QList<QGraphicsItem *>();
2204 return d->scene->items();
2205}
2206
2207/*!
2208 Returns a list of all the items at the position \a pos in the view. The
2209 items are listed in descending stacking order (i.e., the first item in the
2210 list is the uppermost item, and the last item is the lowermost item). \a
2211 pos is in viewport coordinates.
2212
2213 This function is most commonly called from within mouse event handlers in
2214 a subclass in QGraphicsView. \a pos is in untransformed viewport
2215 coordinates, just like QMouseEvent::pos().
2216
2217 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 5
2218
2219 \sa QGraphicsScene::items(), {QGraphicsItem#Sorting}{Sorting}
2220*/
2221QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const
2222{
2223 Q_D(const QGraphicsView);
2224 if (!d->scene)
2225 return QList<QGraphicsItem *>();
2226 // ### Unify these two, and use the items(QPointF) version in
2227 // QGraphicsScene instead. The scene items function could use the viewport
2228 // transform to map the point to a rect/polygon.
2229 if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) {
2230 // Use the rect version
2231 QTransform xinv = viewportTransform().inverted();
2232 return d->scene->items(rect: xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)),
2233 mode: Qt::IntersectsItemShape,
2234 order: Qt::DescendingOrder,
2235 deviceTransform: viewportTransform());
2236 }
2237 // Use the polygon version
2238 return d->scene->items(polygon: mapToScene(ax: pos.x(), ay: pos.y(), w: 1, h: 1),
2239 mode: Qt::IntersectsItemShape,
2240 order: Qt::DescendingOrder,
2241 deviceTransform: viewportTransform());
2242}
2243
2244/*!
2245 \fn QGraphicsView::items(int x, int y) const
2246
2247 This function is provided for convenience. It's equivalent to calling
2248 items(QPoint(\a x, \a y)).
2249*/
2250
2251/*!
2252 \overload
2253
2254 Returns a list of all the items that, depending on \a mode, are either
2255 contained by or intersect with \a rect. \a rect is in viewport
2256 coordinates.
2257
2258 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2259 exact shape intersects with or is contained by \a rect are returned.
2260
2261 The items are sorted in descending stacking order (i.e., the first item in
2262 the returned list is the uppermost item).
2263
2264 \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
2265*/
2266QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelectionMode mode) const
2267{
2268 Q_D(const QGraphicsView);
2269 if (!d->scene)
2270 return QList<QGraphicsItem *>();
2271 return d->scene->items(polygon: mapToScene(rect), mode, order: Qt::DescendingOrder, deviceTransform: viewportTransform());
2272}
2273
2274/*!
2275 \fn QList<QGraphicsItem *> QGraphicsView::items(int x, int y, int w, int h, Qt::ItemSelectionMode mode) const
2276 \since 4.3
2277
2278 This convenience function is equivalent to calling items(QRectF(\a x, \a
2279 y, \a w, \a h), \a mode).
2280*/
2281
2282/*!
2283 \overload
2284
2285 Returns a list of all the items that, depending on \a mode, are either
2286 contained by or intersect with \a polygon. \a polygon is in viewport
2287 coordinates.
2288
2289 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2290 exact shape intersects with or is contained by \a polygon are returned.
2291
2292 The items are sorted by descending stacking order (i.e., the first item in
2293 the returned list is the uppermost item).
2294
2295 \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
2296*/
2297QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSelectionMode mode) const
2298{
2299 Q_D(const QGraphicsView);
2300 if (!d->scene)
2301 return QList<QGraphicsItem *>();
2302 return d->scene->items(polygon: mapToScene(polygon), mode, order: Qt::DescendingOrder, deviceTransform: viewportTransform());
2303}
2304
2305/*!
2306 \overload
2307
2308 Returns a list of all the items that, depending on \a mode, are either
2309 contained by or intersect with \a path. \a path is in viewport
2310 coordinates.
2311
2312 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2313 exact shape intersects with or is contained by \a path are returned.
2314
2315 \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
2316*/
2317QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
2318{
2319 Q_D(const QGraphicsView);
2320 if (!d->scene)
2321 return QList<QGraphicsItem *>();
2322 return d->scene->items(path: mapToScene(path), mode, order: Qt::DescendingOrder, deviceTransform: viewportTransform());
2323}
2324
2325/*!
2326 Returns the item at position \a pos, which is in viewport coordinates.
2327 If there are several items at this position, this function returns
2328 the topmost item.
2329
2330 Example:
2331
2332 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 6
2333
2334 \sa items(), {QGraphicsItem#Sorting}{Sorting}
2335*/
2336QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const
2337{
2338 Q_D(const QGraphicsView);
2339 if (!d->scene)
2340 return nullptr;
2341 const QList<QGraphicsItem *> itemsAtPos = items(pos);
2342 return itemsAtPos.isEmpty() ? nullptr : itemsAtPos.first();
2343}
2344
2345/*!
2346 \overload
2347 \fn QGraphicsItem *QGraphicsView::itemAt(int x, int y) const
2348
2349 This function is provided for convenience. It's equivalent to
2350 calling itemAt(QPoint(\a x, \a y)).
2351*/
2352
2353/*!
2354 Returns the viewport coordinate \a point mapped to scene coordinates.
2355
2356 Note: It can be useful to map the whole rectangle covered by the pixel at
2357 \a point instead of the point itself. To do this, you can call
2358 mapToScene(QRect(\a point, QSize(2, 2))).
2359
2360 \sa mapFromScene()
2361*/
2362QPointF QGraphicsView::mapToScene(const QPoint &point) const
2363{
2364 Q_D(const QGraphicsView);
2365 QPointF p = point;
2366 p.rx() += d->horizontalScroll();
2367 p.ry() += d->verticalScroll();
2368 return d->identityMatrix ? p : d->matrix.inverted().map(p);
2369}
2370
2371/*!
2372 \fn QGraphicsView::mapToScene(int x, int y) const
2373
2374 This function is provided for convenience. It's equivalent to calling
2375 mapToScene(QPoint(\a x, \a y)).
2376*/
2377
2378/*!
2379 Returns the viewport rectangle \a rect mapped to a scene coordinate
2380 polygon.
2381
2382 \sa mapFromScene()
2383*/
2384QPolygonF QGraphicsView::mapToScene(const QRect &rect) const
2385{
2386 Q_D(const QGraphicsView);
2387 if (!rect.isValid())
2388 return QPolygonF();
2389
2390 QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
2391 QRect r = rect.adjusted(xp1: 0, yp1: 0, xp2: 1, yp2: 1);
2392 QPointF tl = scrollOffset + r.topLeft();
2393 QPointF tr = scrollOffset + r.topRight();
2394 QPointF br = scrollOffset + r.bottomRight();
2395 QPointF bl = scrollOffset + r.bottomLeft();
2396
2397 QPolygonF poly(4);
2398 if (!d->identityMatrix) {
2399 QTransform x = d->matrix.inverted();
2400 poly[0] = x.map(p: tl);
2401 poly[1] = x.map(p: tr);
2402 poly[2] = x.map(p: br);
2403 poly[3] = x.map(p: bl);
2404 } else {
2405 poly[0] = tl;
2406 poly[1] = tr;
2407 poly[2] = br;
2408 poly[3] = bl;
2409 }
2410 return poly;
2411}
2412
2413/*!
2414 \fn QGraphicsView::mapToScene(int x, int y, int w, int h) const
2415
2416 This function is provided for convenience. It's equivalent to calling
2417 mapToScene(QRect(\a x, \a y, \a w, \a h)).
2418*/
2419
2420/*!
2421 Returns the viewport polygon \a polygon mapped to a scene coordinate
2422 polygon.
2423
2424 \sa mapFromScene()
2425*/
2426QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const
2427{
2428 QPolygonF poly;
2429 poly.reserve(asize: polygon.size());
2430 for (const QPoint &point : polygon)
2431 poly << mapToScene(point);
2432 return poly;
2433}
2434
2435/*!
2436 Returns the viewport painter path \a path mapped to a scene coordinate
2437 painter path.
2438
2439 \sa mapFromScene()
2440*/
2441QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const
2442{
2443 Q_D(const QGraphicsView);
2444 QTransform matrix = QTransform::fromTranslate(dx: d->horizontalScroll(), dy: d->verticalScroll());
2445 matrix *= d->matrix.inverted();
2446 return matrix.map(p: path);
2447}
2448
2449/*!
2450 Returns the scene coordinate \a point to viewport coordinates.
2451
2452 \sa mapToScene()
2453*/
2454QPoint QGraphicsView::mapFromScene(const QPointF &point) const
2455{
2456 Q_D(const QGraphicsView);
2457 QPointF p = d->identityMatrix ? point : d->matrix.map(p: point);
2458 p.rx() -= d->horizontalScroll();
2459 p.ry() -= d->verticalScroll();
2460 return p.toPoint();
2461}
2462
2463/*!
2464 \fn QGraphicsView::mapFromScene(qreal x, qreal y) const
2465
2466 This function is provided for convenience. It's equivalent to
2467 calling mapFromScene(QPointF(\a x, \a y)).
2468*/
2469
2470/*!
2471 Returns the scene rectangle \a rect to a viewport coordinate
2472 polygon.
2473
2474 \sa mapToScene()
2475*/
2476QPolygon QGraphicsView::mapFromScene(const QRectF &rect) const
2477{
2478 Q_D(const QGraphicsView);
2479 QPointF tl;
2480 QPointF tr;
2481 QPointF br;
2482 QPointF bl;
2483 if (!d->identityMatrix) {
2484 const QTransform &x = d->matrix;
2485 tl = x.map(p: rect.topLeft());
2486 tr = x.map(p: rect.topRight());
2487 br = x.map(p: rect.bottomRight());
2488 bl = x.map(p: rect.bottomLeft());
2489 } else {
2490 tl = rect.topLeft();
2491 tr = rect.topRight();
2492 br = rect.bottomRight();
2493 bl = rect.bottomLeft();
2494 }
2495 QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
2496 tl -= scrollOffset;
2497 tr -= scrollOffset;
2498 br -= scrollOffset;
2499 bl -= scrollOffset;
2500
2501 QPolygon poly(4);
2502 poly[0] = tl.toPoint();
2503 poly[1] = tr.toPoint();
2504 poly[2] = br.toPoint();
2505 poly[3] = bl.toPoint();
2506 return poly;
2507}
2508
2509/*!
2510 \fn QGraphicsView::mapFromScene(qreal x, qreal y, qreal w, qreal h) const
2511
2512 This function is provided for convenience. It's equivalent to
2513 calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)).
2514*/
2515
2516/*!
2517 Returns the scene coordinate polygon \a polygon to a viewport coordinate
2518 polygon.
2519
2520 \sa mapToScene()
2521*/
2522QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const
2523{
2524 QPolygon poly;
2525 poly.reserve(asize: polygon.size());
2526 for (const QPointF &point : polygon)
2527 poly << mapFromScene(point);
2528 return poly;
2529}
2530
2531/*!
2532 Returns the scene coordinate painter path \a path to a viewport coordinate
2533 painter path.
2534
2535 \sa mapToScene()
2536*/
2537QPainterPath QGraphicsView::mapFromScene(const QPainterPath &path) const
2538{
2539 Q_D(const QGraphicsView);
2540 QTransform matrix = d->matrix;
2541 matrix *= QTransform::fromTranslate(dx: -d->horizontalScroll(), dy: -d->verticalScroll());
2542 return matrix.map(p: path);
2543}
2544
2545/*!
2546 \reimp
2547*/
2548QVariant QGraphicsView::inputMethodQuery(Qt::InputMethodQuery query) const
2549{
2550 Q_D(const QGraphicsView);
2551 if (!d->scene)
2552 return QVariant();
2553
2554 QVariant value = d->scene->inputMethodQuery(query);
2555 if (value.userType() == QMetaType::QRectF)
2556 value = d->mapRectFromScene(rect: value.toRectF());
2557 else if (value.userType() == QMetaType::QPointF)
2558 value = mapFromScene(point: value.toPointF());
2559 else if (value.userType() == QMetaType::QRect)
2560 value = d->mapRectFromScene(rect: value.toRect()).toRect();
2561 else if (value.userType() == QMetaType::QPoint)
2562 value = mapFromScene(point: value.toPoint());
2563 return value;
2564}
2565
2566/*!
2567 \property QGraphicsView::backgroundBrush
2568 \brief the background brush of the scene.
2569
2570 This property sets the background brush for the scene in this view. It is
2571 used to override the scene's own background, and defines the behavior of
2572 drawBackground(). To provide custom background drawing for this view, you
2573 can reimplement drawBackground() instead.
2574
2575 By default, this property contains a brush with the Qt::NoBrush pattern.
2576
2577 \sa QGraphicsScene::backgroundBrush, foregroundBrush
2578*/
2579QBrush QGraphicsView::backgroundBrush() const
2580{
2581 Q_D(const QGraphicsView);
2582 return d->backgroundBrush;
2583}
2584void QGraphicsView::setBackgroundBrush(const QBrush &brush)
2585{
2586 Q_D(QGraphicsView);
2587 d->backgroundBrush = brush;
2588 d->updateAll();
2589
2590 if (d->cacheMode & CacheBackground) {
2591 // Invalidate the background pixmap
2592 d->mustResizeBackgroundPixmap = true;
2593 }
2594}
2595
2596/*!
2597 \property QGraphicsView::foregroundBrush
2598 \brief the foreground brush of the scene.
2599
2600 This property sets the foreground brush for the scene in this view. It is
2601 used to override the scene's own foreground, and defines the behavior of
2602 drawForeground(). To provide custom foreground drawing for this view, you
2603 can reimplement drawForeground() instead.
2604
2605 By default, this property contains a brush with the Qt::NoBrush pattern.
2606
2607 \sa QGraphicsScene::foregroundBrush, backgroundBrush
2608*/
2609QBrush QGraphicsView::foregroundBrush() const
2610{
2611 Q_D(const QGraphicsView);
2612 return d->foregroundBrush;
2613}
2614void QGraphicsView::setForegroundBrush(const QBrush &brush)
2615{
2616 Q_D(QGraphicsView);
2617 d->foregroundBrush = brush;
2618 d->updateAll();
2619}
2620
2621/*!
2622 Schedules an update of the scene rectangles \a rects.
2623
2624 \sa QGraphicsScene::changed()
2625*/
2626void QGraphicsView::updateScene(const QList<QRectF> &rects)
2627{
2628 // ### Note: Since 4.5, this slot is only called if the user explicitly
2629 // establishes a connection between the scene and the view, as the scene
2630 // and view are no longer connected. We need to keep it working (basically
2631 // leave it as it is), but the new delivery path is through
2632 // QGraphicsScenePrivate::itemUpdate().
2633 Q_D(QGraphicsView);
2634 if (d->fullUpdatePending || d->viewportUpdateMode == QGraphicsView::NoViewportUpdate)
2635 return;
2636
2637 // Extract and reset dirty scene rect info.
2638 QList<QRect> dirtyViewportRects;
2639 dirtyViewportRects.reserve(asize: d->dirtyRegion.rectCount() + rects.size());
2640 for (const QRect &dirtyRect : d->dirtyRegion)
2641 dirtyViewportRects += dirtyRect;
2642 d->dirtyRegion = QRegion();
2643 d->dirtyBoundingRect = QRect();
2644
2645 bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate;
2646 bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate)
2647 || (d->viewportUpdateMode == QGraphicsView::SmartViewportUpdate
2648 && ((dirtyViewportRects.size() + rects.size()) >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD));
2649
2650 QRegion updateRegion;
2651 QRect boundingRect;
2652 QRect viewportRect = viewport()->rect();
2653 bool redraw = false;
2654 QTransform transform = viewportTransform();
2655
2656 // Convert scene rects to viewport rects.
2657 for (const QRectF &rect : rects) {
2658 QRect xrect = transform.mapRect(rect).toAlignedRect();
2659 if (!(d->optimizationFlags & DontAdjustForAntialiasing))
2660 xrect.adjust(dx1: -2, dy1: -2, dx2: 2, dy2: 2);
2661 else
2662 xrect.adjust(dx1: -1, dy1: -1, dx2: 1, dy2: 1);
2663 if (!viewportRect.intersects(r: xrect))
2664 continue;
2665 dirtyViewportRects << xrect;
2666 }
2667
2668 for (const QRect &rect : std::as_const(t&: dirtyViewportRects)) {
2669 // Add the exposed rect to the update region. In rect update
2670 // mode, we only count the bounding rect of items.
2671 if (!boundingRectUpdate) {
2672 updateRegion += rect;
2673 } else {
2674 boundingRect |= rect;
2675 }
2676 redraw = true;
2677 if (fullUpdate) {
2678 // If fullUpdate is true and we found a visible dirty rect,
2679 // we're done.
2680 break;
2681 }
2682 }
2683
2684 if (!redraw)
2685 return;
2686
2687 if (fullUpdate)
2688 viewport()->update();
2689 else if (boundingRectUpdate)
2690 viewport()->update(boundingRect);
2691 else
2692 viewport()->update(updateRegion);
2693}
2694
2695/*!
2696 Notifies QGraphicsView that the scene's scene rect has changed. \a rect
2697 is the new scene rect. If the view already has an explicitly set scene
2698 rect, this function does nothing.
2699
2700 \sa sceneRect, QGraphicsScene::sceneRectChanged()
2701*/
2702void QGraphicsView::updateSceneRect(const QRectF &rect)
2703{
2704 Q_D(QGraphicsView);
2705 if (!d->hasSceneRect) {
2706 d->sceneRect = rect;
2707 d->recalculateContentSize();
2708 }
2709}
2710
2711/*!
2712 This slot is called by QAbstractScrollArea after setViewport() has been
2713 called. Reimplement this function in a subclass of QGraphicsView to
2714 initialize the new viewport \a widget before it is used.
2715
2716 \sa setViewport()
2717*/
2718void QGraphicsView::setupViewport(QWidget *widget)
2719{
2720 Q_D(QGraphicsView);
2721
2722 if (!widget) {
2723 qWarning(msg: "QGraphicsView::setupViewport: cannot initialize null widget");
2724 return;
2725 }
2726
2727 const bool isGLWidget = widget->inherits(classname: "QOpenGLWidget");
2728
2729 d->accelerateScrolling = !(isGLWidget);
2730
2731 widget->setFocusPolicy(Qt::StrongFocus);
2732
2733 if (isGLWidget)
2734 d->stereoEnabled = QWidgetPrivate::get(w: widget)->isStereoEnabled();
2735
2736 if (!isGLWidget) {
2737 // autoFillBackground enables scroll acceleration.
2738 widget->setAutoFillBackground(true);
2739 }
2740
2741 // We are only interested in mouse tracking if items
2742 // accept hover events or use non-default cursors or if
2743 // AnchorUnderMouse is used as transformation or resize anchor.
2744 if ((d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents
2745 || !d->scene->d_func()->allItemsUseDefaultCursor))
2746 || d->transformationAnchor == AnchorUnderMouse
2747 || d->resizeAnchor == AnchorUnderMouse) {
2748 widget->setMouseTracking(true);
2749 }
2750
2751 // enable touch events if any items is interested in them
2752 if (d->scene && !d->scene->d_func()->allItemsIgnoreTouchEvents)
2753 widget->setAttribute(Qt::WA_AcceptTouchEvents);
2754
2755#ifndef QT_NO_GESTURES
2756 if (d->scene) {
2757 const auto gestures = d->scene->d_func()->grabbedGestures.keys();
2758 for (Qt::GestureType gesture : gestures)
2759 widget->grabGesture(type: gesture);
2760 }
2761#endif
2762
2763 widget->setAcceptDrops(acceptDrops());
2764}
2765
2766/*!
2767 \reimp
2768*/
2769bool QGraphicsView::event(QEvent *event)
2770{
2771 Q_D(QGraphicsView);
2772
2773 if (d->sceneInteractionAllowed) {
2774 switch (event->type()) {
2775 case QEvent::ShortcutOverride:
2776 if (d->scene)
2777 return QCoreApplication::sendEvent(receiver: d->scene, event);
2778 break;
2779 case QEvent::KeyPress:
2780 if (d->scene) {
2781 QKeyEvent *k = static_cast<QKeyEvent *>(event);
2782 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
2783 // Send the key events to the scene. This will invoke the
2784 // scene's tab focus handling, and if the event is
2785 // accepted, we return (prevent further event delivery),
2786 // and the base implementation will call QGraphicsView's
2787 // focusNextPrevChild() function. If the event is ignored,
2788 // we fall back to standard tab focus handling.
2789 QCoreApplication::sendEvent(receiver: d->scene, event);
2790 if (event->isAccepted())
2791 return true;
2792 // Ensure the event doesn't propagate just because the
2793 // scene ignored it. If the event propagates, then tab
2794 // handling will be called twice (this and parent).
2795 event->accept();
2796 }
2797 }
2798 break;
2799 default:
2800 break;
2801 }
2802 }
2803
2804 return QAbstractScrollArea::event(event);
2805}
2806
2807/*!
2808 \reimp
2809*/
2810bool QGraphicsView::viewportEvent(QEvent *event)
2811{
2812 Q_D(QGraphicsView);
2813 if (!d->scene)
2814 return QAbstractScrollArea::viewportEvent(event);
2815
2816 switch (event->type()) {
2817 case QEvent::Enter:
2818 QCoreApplication::sendEvent(receiver: d->scene, event);
2819 break;
2820 case QEvent::WindowActivate:
2821 QCoreApplication::sendEvent(receiver: d->scene, event);
2822 break;
2823 case QEvent::WindowDeactivate:
2824 // ### This is a temporary fix for until we get proper mouse
2825 // grab events. mouseGrabberItem should be set to 0 if we lose
2826 // the mouse grab.
2827 // Remove all popups when the scene loses focus.
2828 if (!d->scene->d_func()->popupWidgets.isEmpty())
2829 d->scene->d_func()->removePopup(widget: d->scene->d_func()->popupWidgets.constFirst());
2830 QCoreApplication::sendEvent(receiver: d->scene, event);
2831 break;
2832 case QEvent::Show:
2833 if (d->scene && isActiveWindow()) {
2834 QEvent windowActivate(QEvent::WindowActivate);
2835 QCoreApplication::sendEvent(receiver: d->scene, event: &windowActivate);
2836 }
2837 break;
2838 case QEvent::Hide:
2839 // spontaneous event will generate a WindowDeactivate.
2840 if (!event->spontaneous() && d->scene && isActiveWindow()) {
2841 QEvent windowDeactivate(QEvent::WindowDeactivate);
2842 QCoreApplication::sendEvent(receiver: d->scene, event: &windowDeactivate);
2843 }
2844 break;
2845 case QEvent::Leave: {
2846 // ### This is a temporary fix for until we get proper mouse grab
2847 // events. activeMouseGrabberItem should be set to 0 if we lose the
2848 // mouse grab.
2849 if ((QApplication::activePopupWidget() && QApplication::activePopupWidget() != window())
2850 || (QApplication::activeModalWidget() && QApplication::activeModalWidget() != window())
2851 || (QApplication::activeWindow() != window())) {
2852 if (!d->scene->d_func()->popupWidgets.isEmpty())
2853 d->scene->d_func()->removePopup(widget: d->scene->d_func()->popupWidgets.constFirst());
2854 }
2855 d->useLastMouseEvent = false;
2856 QGraphicsSceneEvent leaveEvent(QEvent::GraphicsSceneLeave);
2857 leaveEvent.setWidget(viewport());
2858 QCoreApplication::sendEvent(receiver: d->scene, event: &leaveEvent);
2859 event->setAccepted(leaveEvent.isAccepted());
2860 break;
2861 }
2862#if QT_CONFIG(tooltip)
2863 case QEvent::ToolTip: {
2864 QHelpEvent *toolTip = static_cast<QHelpEvent *>(event);
2865 QGraphicsSceneHelpEvent helpEvent(QEvent::GraphicsSceneHelp);
2866 helpEvent.setWidget(viewport());
2867 helpEvent.setScreenPos(toolTip->globalPos());
2868 helpEvent.setScenePos(mapToScene(point: toolTip->pos()));
2869 QCoreApplication::sendEvent(receiver: d->scene, event: &helpEvent);
2870 toolTip->setAccepted(helpEvent.isAccepted());
2871 return true;
2872 }
2873#endif
2874 case QEvent::Paint:
2875 // Reset full update
2876 d->fullUpdatePending = false;
2877 d->dirtyScrollOffset = QPoint();
2878 if (d->scene) {
2879 // Check if this view reimplements the updateScene slot; if it
2880 // does, we can't do direct update delivery and have to fall back
2881 // to connecting the changed signal.
2882 if (!d->updateSceneSlotReimplementedChecked) {
2883 d->updateSceneSlotReimplementedChecked = true;
2884 const QMetaObject *mo = metaObject();
2885 if (mo != &QGraphicsView::staticMetaObject) {
2886 if (mo->indexOfSlot(slot: "updateScene(QList<QRectF>)")
2887 != QGraphicsView::staticMetaObject.indexOfSlot(slot: "updateScene(QList<QRectF>)")) {
2888 connect(sender: d->scene, SIGNAL(changed(QList<QRectF>)),
2889 receiver: this, SLOT(updateScene(QList<QRectF>)));
2890 }
2891 }
2892 }
2893 }
2894 break;
2895 case QEvent::TouchBegin:
2896 case QEvent::TouchUpdate:
2897 case QEvent::TouchEnd:
2898 {
2899 if (!isEnabled())
2900 return false;
2901
2902 if (d->scene && d->sceneInteractionAllowed) {
2903 // Convert and deliver the touch event to the scene.
2904 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
2905 QMutableTouchEvent::from(e: touchEvent)->setTarget(viewport());
2906 QGraphicsViewPrivate::translateTouchEvent(d, touchEvent);
2907 QCoreApplication::sendEvent(receiver: d->scene, event: touchEvent);
2908 } else {
2909 event->ignore();
2910 }
2911
2912 return true;
2913 }
2914#ifndef QT_NO_GESTURES
2915 case QEvent::Gesture:
2916 case QEvent::GestureOverride:
2917 {
2918 if (!isEnabled())
2919 return false;
2920
2921 if (d->scene && d->sceneInteractionAllowed) {
2922 QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event);
2923 gestureEvent->setWidget(viewport());
2924 QCoreApplication::sendEvent(receiver: d->scene, event: gestureEvent);
2925 }
2926 return true;
2927 }
2928#endif // QT_NO_GESTURES
2929 default:
2930 break;
2931 }
2932
2933 return QAbstractScrollArea::viewportEvent(event);
2934}
2935
2936#ifndef QT_NO_CONTEXTMENU
2937/*!
2938 \reimp
2939*/
2940void QGraphicsView::contextMenuEvent(QContextMenuEvent *event)
2941{
2942 Q_D(QGraphicsView);
2943 if (!d->scene || !d->sceneInteractionAllowed)
2944 return;
2945
2946 d->mousePressViewPoint = event->pos();
2947 d->mousePressScenePoint = mapToScene(point: d->mousePressViewPoint);
2948 d->mousePressScreenPoint = event->globalPos();
2949 d->lastMouseMoveScenePoint = d->mousePressScenePoint;
2950 d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
2951
2952 QGraphicsSceneContextMenuEvent contextEvent(QEvent::GraphicsSceneContextMenu);
2953 contextEvent.setWidget(viewport());
2954 contextEvent.setScenePos(d->mousePressScenePoint);
2955 contextEvent.setScreenPos(d->mousePressScreenPoint);
2956 contextEvent.setModifiers(event->modifiers());
2957 contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason()));
2958 contextEvent.setAccepted(event->isAccepted());
2959 contextEvent.setTimestamp(event->timestamp());
2960 QCoreApplication::sendEvent(receiver: d->scene, event: &contextEvent);
2961 event->setAccepted(contextEvent.isAccepted());
2962}
2963#endif // QT_NO_CONTEXTMENU
2964
2965#if QT_CONFIG(draganddrop)
2966/*!
2967 \reimp
2968*/
2969void QGraphicsView::dropEvent(QDropEvent *event)
2970{
2971 Q_D(QGraphicsView);
2972 if (!d->scene || !d->sceneInteractionAllowed)
2973 return;
2974
2975 // Generate a scene event.
2976 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDrop);
2977 d->populateSceneDragDropEvent(dest: &sceneEvent, source: event);
2978
2979 // Send it to the scene.
2980 QCoreApplication::sendEvent(receiver: d->scene, event: &sceneEvent);
2981
2982 // Accept the originating event if the scene accepted the scene event.
2983 event->setAccepted(sceneEvent.isAccepted());
2984 if (sceneEvent.isAccepted())
2985 event->setDropAction(sceneEvent.dropAction());
2986
2987 delete d->lastDragDropEvent;
2988 d->lastDragDropEvent = nullptr;
2989}
2990
2991/*!
2992 \reimp
2993*/
2994void QGraphicsView::dragEnterEvent(QDragEnterEvent *event)
2995{
2996 Q_D(QGraphicsView);
2997 if (!d->scene || !d->sceneInteractionAllowed)
2998 return;
2999
3000 // Disable replaying of mouse move events.
3001 d->useLastMouseEvent = false;
3002
3003 // Generate a scene event.
3004 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragEnter);
3005 d->populateSceneDragDropEvent(dest: &sceneEvent, source: event);
3006
3007 // Store it for later use.
3008 d->storeDragDropEvent(event: &sceneEvent);
3009
3010 // Send it to the scene.
3011 QCoreApplication::sendEvent(receiver: d->scene, event: &sceneEvent);
3012
3013 // Accept the originating event if the scene accepted the scene event.
3014 if (sceneEvent.isAccepted()) {
3015 event->setAccepted(true);
3016 event->setDropAction(sceneEvent.dropAction());
3017 }
3018}
3019
3020/*!
3021 \reimp
3022*/
3023void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
3024{
3025 Q_D(QGraphicsView);
3026 if (!d->scene || !d->sceneInteractionAllowed)
3027 return;
3028 if (!d->lastDragDropEvent) {
3029 qWarning(msg: "QGraphicsView::dragLeaveEvent: drag leave received before drag enter");
3030 return;
3031 }
3032
3033 // Generate a scene event.
3034 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragLeave);
3035 sceneEvent.setScenePos(d->lastDragDropEvent->scenePos());
3036 sceneEvent.setScreenPos(d->lastDragDropEvent->screenPos());
3037 sceneEvent.setButtons(d->lastDragDropEvent->buttons());
3038 sceneEvent.setModifiers(d->lastDragDropEvent->modifiers());
3039 sceneEvent.setPossibleActions(d->lastDragDropEvent->possibleActions());
3040 sceneEvent.setProposedAction(d->lastDragDropEvent->proposedAction());
3041 sceneEvent.setDropAction(d->lastDragDropEvent->dropAction());
3042 sceneEvent.setMimeData(d->lastDragDropEvent->mimeData());
3043 sceneEvent.setWidget(d->lastDragDropEvent->widget());
3044 sceneEvent.setSource(d->lastDragDropEvent->source());
3045 delete d->lastDragDropEvent;
3046 d->lastDragDropEvent = nullptr;
3047
3048 // Send it to the scene.
3049 QCoreApplication::sendEvent(receiver: d->scene, event: &sceneEvent);
3050
3051 // Accept the originating event if the scene accepted the scene event.
3052 if (sceneEvent.isAccepted())
3053 event->setAccepted(true);
3054}
3055
3056/*!
3057 \reimp
3058*/
3059void QGraphicsView::dragMoveEvent(QDragMoveEvent *event)
3060{
3061 Q_D(QGraphicsView);
3062 if (!d->scene || !d->sceneInteractionAllowed)
3063 return;
3064
3065 // Generate a scene event.
3066 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragMove);
3067 d->populateSceneDragDropEvent(dest: &sceneEvent, source: event);
3068
3069 // Store it for later use.
3070 d->storeDragDropEvent(event: &sceneEvent);
3071
3072 // Send it to the scene.
3073 QCoreApplication::sendEvent(receiver: d->scene, event: &sceneEvent);
3074
3075 // Ignore the originating event if the scene ignored the scene event.
3076 event->setAccepted(sceneEvent.isAccepted());
3077 if (sceneEvent.isAccepted())
3078 event->setDropAction(sceneEvent.dropAction());
3079}
3080#endif // QT_CONFIG(draganddrop)
3081
3082/*!
3083 \reimp
3084*/
3085void QGraphicsView::focusInEvent(QFocusEvent *event)
3086{
3087 Q_D(QGraphicsView);
3088 d->updateInputMethodSensitivity();
3089 QAbstractScrollArea::focusInEvent(event);
3090 if (d->scene)
3091 QCoreApplication::sendEvent(receiver: d->scene, event);
3092 // Pass focus on if the scene cannot accept focus.
3093 if (!d->scene || !event->isAccepted())
3094 QAbstractScrollArea::focusInEvent(event);
3095}
3096
3097/*!
3098 \reimp
3099*/
3100bool QGraphicsView::focusNextPrevChild(bool next)
3101{
3102 return QAbstractScrollArea::focusNextPrevChild(next);
3103}
3104
3105/*!
3106 \reimp
3107*/
3108void QGraphicsView::focusOutEvent(QFocusEvent *event)
3109{
3110 Q_D(QGraphicsView);
3111 QAbstractScrollArea::focusOutEvent(event);
3112 if (d->scene)
3113 QCoreApplication::sendEvent(receiver: d->scene, event);
3114}
3115
3116/*!
3117 \reimp
3118*/
3119void QGraphicsView::keyPressEvent(QKeyEvent *event)
3120{
3121 Q_D(QGraphicsView);
3122 if (!d->scene || !d->sceneInteractionAllowed) {
3123 QAbstractScrollArea::keyPressEvent(event);
3124 return;
3125 }
3126 QCoreApplication::sendEvent(receiver: d->scene, event);
3127 if (!event->isAccepted())
3128 QAbstractScrollArea::keyPressEvent(event);
3129}
3130
3131/*!
3132 \reimp
3133*/
3134void QGraphicsView::keyReleaseEvent(QKeyEvent *event)
3135{
3136 Q_D(QGraphicsView);
3137 if (!d->scene || !d->sceneInteractionAllowed)
3138 return;
3139 QCoreApplication::sendEvent(receiver: d->scene, event);
3140 if (!event->isAccepted())
3141 QAbstractScrollArea::keyReleaseEvent(event);
3142}
3143
3144/*!
3145 \reimp
3146*/
3147void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
3148{
3149 Q_D(QGraphicsView);
3150 if (!d->scene || !d->sceneInteractionAllowed)
3151 return;
3152
3153 d->storeMouseEvent(event);
3154 d->mousePressViewPoint = event->position().toPoint();
3155 d->mousePressScenePoint = mapToScene(point: d->mousePressViewPoint);
3156 d->mousePressScreenPoint = event->globalPosition().toPoint();
3157 d->lastMouseMoveScenePoint = d->mousePressScenePoint;
3158 d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
3159 d->mousePressButton = event->button();
3160
3161 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);
3162 mouseEvent.setWidget(viewport());
3163 mouseEvent.setButtonDownScenePos(button: d->mousePressButton, pos: d->mousePressScenePoint);
3164 mouseEvent.setButtonDownScreenPos(button: d->mousePressButton, pos: d->mousePressScreenPoint);
3165 mouseEvent.setScenePos(mapToScene(point: d->mousePressViewPoint));
3166 mouseEvent.setScreenPos(d->mousePressScreenPoint);
3167 mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
3168 mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
3169 mouseEvent.setButtons(event->buttons());
3170 mouseEvent.setAccepted(false);
3171 mouseEvent.setButton(event->button());
3172 mouseEvent.setModifiers(event->modifiers());
3173 mouseEvent.setSource(event->source());
3174 mouseEvent.setFlags(event->flags());
3175 mouseEvent.setTimestamp(event->timestamp());
3176 if (event->spontaneous())
3177 qt_sendSpontaneousEvent(receiver: d->scene, event: &mouseEvent);
3178 else
3179 QCoreApplication::sendEvent(receiver: d->scene, event: &mouseEvent);
3180
3181 // Update the original mouse event accepted state.
3182 const bool isAccepted = mouseEvent.isAccepted();
3183 event->setAccepted(isAccepted);
3184
3185 // Update the last mouse event accepted state.
3186 d->lastMouseEvent.setAccepted(isAccepted);
3187}
3188
3189/*!
3190 \reimp
3191*/
3192void QGraphicsView::mousePressEvent(QMouseEvent *event)
3193{
3194 Q_D(QGraphicsView);
3195
3196 // Store this event for replaying, finding deltas, and for
3197 // scroll-dragging; even in non-interactive mode, scroll hand dragging is
3198 // allowed, so we store the event at the very top of this function.
3199 d->storeMouseEvent(event);
3200 d->lastMouseEvent.setAccepted(false);
3201
3202 if (d->sceneInteractionAllowed) {
3203 // Store some of the event's button-down data.
3204 d->mousePressViewPoint = event->position().toPoint();
3205 d->mousePressScenePoint = mapToScene(point: d->mousePressViewPoint);
3206 d->mousePressScreenPoint = event->globalPosition().toPoint();
3207 d->lastMouseMoveScenePoint = d->mousePressScenePoint;
3208 d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
3209 d->mousePressButton = event->button();
3210
3211 if (d->scene) {
3212 // Convert and deliver the mouse event to the scene.
3213 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
3214 mouseEvent.setWidget(viewport());
3215 mouseEvent.setButtonDownScenePos(button: d->mousePressButton, pos: d->mousePressScenePoint);
3216 mouseEvent.setButtonDownScreenPos(button: d->mousePressButton, pos: d->mousePressScreenPoint);
3217 mouseEvent.setScenePos(d->mousePressScenePoint);
3218 mouseEvent.setScreenPos(d->mousePressScreenPoint);
3219 mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
3220 mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
3221 mouseEvent.setButtons(event->buttons());
3222 mouseEvent.setButton(event->button());
3223 mouseEvent.setModifiers(event->modifiers());
3224 mouseEvent.setSource(event->source());
3225 mouseEvent.setFlags(event->flags());
3226 mouseEvent.setAccepted(false);
3227 mouseEvent.setTimestamp(event->timestamp());
3228 if (event->spontaneous())
3229 qt_sendSpontaneousEvent(receiver: d->scene, event: &mouseEvent);
3230 else
3231 QCoreApplication::sendEvent(receiver: d->scene, event: &mouseEvent);
3232
3233 // Update the original mouse event accepted state.
3234 bool isAccepted = mouseEvent.isAccepted();
3235 event->setAccepted(isAccepted);
3236
3237 // Update the last mouse event accepted state.
3238 d->lastMouseEvent.setAccepted(isAccepted);
3239
3240 if (isAccepted)
3241 return;
3242 }
3243 }
3244
3245#if QT_CONFIG(rubberband)
3246 if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) {
3247 if (d->sceneInteractionAllowed) {
3248 // Rubberbanding is only allowed in interactive mode.
3249 event->accept();
3250 d->rubberBanding = true;
3251 d->rubberBandRect = QRect();
3252 if (d->scene) {
3253 bool extendSelection = (event->modifiers() & Qt::ControlModifier) != 0;
3254
3255 if (extendSelection) {
3256 d->rubberBandSelectionOperation = Qt::AddToSelection;
3257 } else {
3258 d->rubberBandSelectionOperation = Qt::ReplaceSelection;
3259 d->scene->clearSelection();
3260 }
3261 }
3262 }
3263 } else
3264#endif
3265 if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
3266 // Left-button press in scroll hand mode initiates hand scrolling.
3267 event->accept();
3268 d->handScrolling = true;
3269 d->handScrollMotions = 0;
3270#ifndef QT_NO_CURSOR
3271 viewport()->setCursor(Qt::ClosedHandCursor);
3272#endif
3273 }
3274}
3275
3276/*!
3277 \reimp
3278*/
3279void QGraphicsView::mouseMoveEvent(QMouseEvent *event)
3280{
3281 Q_D(QGraphicsView);
3282
3283 if (d->dragMode == QGraphicsView::ScrollHandDrag) {
3284 if (d->handScrolling) {
3285 QScrollBar *hBar = horizontalScrollBar();
3286 QScrollBar *vBar = verticalScrollBar();
3287 QPoint delta = event->position().toPoint() - d->lastMouseEvent.position().toPoint();
3288 hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
3289 vBar->setValue(vBar->value() - delta.y());
3290
3291 // Detect how much we've scrolled to disambiguate scrolling from
3292 // clicking.
3293 ++d->handScrollMotions;
3294 }
3295 }
3296
3297 d->mouseMoveEventHandler(event);
3298}
3299
3300/*!
3301 \reimp
3302*/
3303void QGraphicsView::mouseReleaseEvent(QMouseEvent *event)
3304{
3305 Q_D(QGraphicsView);
3306
3307#if QT_CONFIG(rubberband)
3308 if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed && !event->buttons()) {
3309 d->clearRubberBand();
3310 } else
3311#endif
3312 if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
3313#ifndef QT_NO_CURSOR
3314 // Restore the open hand cursor. ### There might be items
3315 // under the mouse that have a valid cursor at this time, so
3316 // we could repeat the steps from mouseMoveEvent().
3317 viewport()->setCursor(Qt::OpenHandCursor);
3318#endif
3319 d->handScrolling = false;
3320
3321 if (d->scene && d->sceneInteractionAllowed && !d->lastMouseEvent.isAccepted() && d->handScrollMotions <= 6) {
3322 // If we've detected very little motion during the hand drag, and
3323 // no item accepted the last event, we'll interpret that as a
3324 // click to the scene, and reset the selection.
3325 d->scene->clearSelection();
3326 }
3327 }
3328
3329 d->storeMouseEvent(event);
3330
3331 if (!d->sceneInteractionAllowed)
3332 return;
3333
3334 if (!d->scene)
3335 return;
3336
3337 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
3338 mouseEvent.setWidget(viewport());
3339 mouseEvent.setButtonDownScenePos(button: d->mousePressButton, pos: d->mousePressScenePoint);
3340 mouseEvent.setButtonDownScreenPos(button: d->mousePressButton, pos: d->mousePressScreenPoint);
3341 mouseEvent.setScenePos(mapToScene(point: event->position().toPoint()));
3342 mouseEvent.setScreenPos(event->globalPosition().toPoint());
3343 mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
3344 mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
3345 mouseEvent.setButtons(event->buttons());
3346 mouseEvent.setButton(event->button());
3347 mouseEvent.setModifiers(event->modifiers());
3348 mouseEvent.setSource(event->source());
3349 mouseEvent.setFlags(event->flags());
3350 mouseEvent.setAccepted(false);
3351 mouseEvent.setTimestamp(event->timestamp());
3352 if (event->spontaneous())
3353 qt_sendSpontaneousEvent(receiver: d->scene, event: &mouseEvent);
3354 else
3355 QCoreApplication::sendEvent(receiver: d->scene, event: &mouseEvent);
3356
3357 // Update the last mouse event selected state.
3358 d->lastMouseEvent.setAccepted(mouseEvent.isAccepted());
3359
3360#ifndef QT_NO_CURSOR
3361 if (mouseEvent.isAccepted() && mouseEvent.buttons() == 0 && viewport()->testAttribute(attribute: Qt::WA_SetCursor)) {
3362 // The last mouse release on the viewport will trigger clearing the cursor.
3363 d->_q_unsetViewportCursor();
3364 }
3365#endif
3366}
3367
3368#if QT_CONFIG(wheelevent)
3369/*!
3370 \reimp
3371*/
3372void QGraphicsView::wheelEvent(QWheelEvent *event)
3373{
3374 Q_D(QGraphicsView);
3375 if (!d->scene || !d->sceneInteractionAllowed) {
3376 QAbstractScrollArea::wheelEvent(event);
3377 return;
3378 }
3379
3380 event->ignore();
3381
3382 QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel);
3383 wheelEvent.setWidget(viewport());
3384 wheelEvent.setScenePos(mapToScene(point: event->position().toPoint()));
3385 wheelEvent.setScreenPos(event->globalPosition().toPoint());
3386 wheelEvent.setButtons(event->buttons());
3387 wheelEvent.setModifiers(event->modifiers());
3388 const bool horizontal = qAbs(t: event->angleDelta().x()) > qAbs(t: event->angleDelta().y());
3389 wheelEvent.setDelta(horizontal ? event->angleDelta().x() : event->angleDelta().y());
3390 wheelEvent.setPixelDelta(event->pixelDelta());
3391 wheelEvent.setPhase(event->phase());
3392 wheelEvent.setInverted(event->isInverted());
3393 wheelEvent.setOrientation(horizontal ? Qt::Horizontal : Qt::Vertical);
3394 wheelEvent.setAccepted(false);
3395 wheelEvent.setTimestamp(event->timestamp());
3396 QCoreApplication::sendEvent(receiver: d->scene, event: &wheelEvent);
3397 event->setAccepted(wheelEvent.isAccepted());
3398 if (!event->isAccepted())
3399 QAbstractScrollArea::wheelEvent(event);
3400}
3401#endif // QT_CONFIG(wheelevent)
3402
3403/*!
3404 \reimp
3405*/
3406void QGraphicsView::paintEvent(QPaintEvent *event)
3407{
3408 Q_D(QGraphicsView);
3409 if (!d->scene) {
3410 QAbstractScrollArea::paintEvent(event);
3411 return;
3412 }
3413
3414 // Set up painter state protection.
3415 d->scene->d_func()->painterStateProtection = !(d->optimizationFlags & DontSavePainterState);
3416
3417 // Determine the exposed region
3418 d->exposedRegion = event->region();
3419 QRectF exposedSceneRect = mapToScene(rect: d->exposedRegion.boundingRect()).boundingRect();
3420
3421 // Set up the painter
3422 QPainter painter(viewport());
3423#if QT_CONFIG(rubberband)
3424 if (d->rubberBanding && !d->rubberBandRect.isEmpty())
3425 painter.save();
3426#endif
3427 // Set up render hints
3428 painter.setRenderHints(hints: painter.renderHints(), on: false);
3429 painter.setRenderHints(hints: d->renderHints, on: true);
3430
3431 // Set up viewport transform
3432 const bool viewTransformed = isTransformed();
3433 if (viewTransformed)
3434 painter.setWorldTransform(matrix: viewportTransform());
3435 const QTransform viewTransform = painter.worldTransform();
3436
3437 const auto actuallyDraw = [&]() {
3438 // Draw background
3439 if (d->cacheMode & CacheBackground) {
3440 // Recreate the background pixmap, and flag the whole background as
3441 // exposed.
3442 if (d->mustResizeBackgroundPixmap) {
3443 const qreal dpr = d->viewport->devicePixelRatio();
3444 d->backgroundPixmap = QPixmap(viewport()->size() * dpr);
3445 d->backgroundPixmap.setDevicePixelRatio(dpr);
3446 QBrush bgBrush = viewport()->palette().brush(cr: viewport()->backgroundRole());
3447 if (!bgBrush.isOpaque())
3448 d->backgroundPixmap.fill(fillColor: Qt::transparent);
3449 QPainter p(&d->backgroundPixmap);
3450 p.fillRect(x: 0, y: 0, w: d->backgroundPixmap.width(), h: d->backgroundPixmap.height(), b: bgBrush);
3451 d->backgroundPixmapExposed = QRegion(viewport()->rect());
3452 d->mustResizeBackgroundPixmap = false;
3453 }
3454
3455 // Redraw exposed areas
3456 if (!d->backgroundPixmapExposed.isEmpty()) {
3457 QPainter backgroundPainter(&d->backgroundPixmap);
3458 backgroundPainter.setClipRegion(d->backgroundPixmapExposed, op: Qt::ReplaceClip);
3459 if (viewTransformed)
3460 backgroundPainter.setTransform(transform: viewTransform);
3461 QRectF backgroundExposedSceneRect = mapToScene(rect: d->backgroundPixmapExposed.boundingRect()).boundingRect();
3462 drawBackground(painter: &backgroundPainter, rect: backgroundExposedSceneRect);
3463 d->backgroundPixmapExposed = QRegion();
3464 }
3465
3466 // Blit the background from the background pixmap
3467 if (viewTransformed) {
3468 painter.setWorldTransform(matrix: QTransform());
3469 painter.drawPixmap(p: QPoint(), pm: d->backgroundPixmap);
3470 painter.setWorldTransform(matrix: viewTransform);
3471 } else {
3472 painter.drawPixmap(p: QPoint(), pm: d->backgroundPixmap);
3473 }
3474 } else {
3475 if (!(d->optimizationFlags & DontSavePainterState))
3476 painter.save();
3477
3478 drawBackground(painter: &painter, rect: exposedSceneRect);
3479 if (!(d->optimizationFlags & DontSavePainterState))
3480 painter.restore();
3481 }
3482
3483 // Items
3484 if (!(d->optimizationFlags & IndirectPainting)) {
3485 const quint32 oldRectAdjust = d->scene->d_func()->rectAdjust;
3486 if (d->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
3487 d->scene->d_func()->rectAdjust = 1;
3488 else
3489 d->scene->d_func()->rectAdjust = 2;
3490 d->scene->d_func()->drawItems(painter: &painter, viewTransform: viewTransformed ? &viewTransform : nullptr,
3491 exposedRegion: &d->exposedRegion, widget: viewport());
3492 d->scene->d_func()->rectAdjust = oldRectAdjust;
3493 // Make sure the painter's world transform is restored correctly when
3494 // drawing without painter state protection (DontSavePainterState).
3495 // We only change the worldTransform() so there's no need to do a full-blown
3496 // save() and restore(). Also note that we don't have to do this in case of
3497 // IndirectPainting (the else branch), because in that case we always save()
3498 // and restore() in QGraphicsScene::drawItems().
3499 if (!d->scene->d_func()->painterStateProtection)
3500 painter.setOpacity(1.0);
3501 painter.setWorldTransform(matrix: viewTransform);
3502 } else {
3503 // Make sure we don't have unpolished items before we draw
3504 if (!d->scene->d_func()->unpolishedItems.isEmpty())
3505 d->scene->d_func()->_q_polishItems();
3506 // We reset updateAll here (after we've issued polish events)
3507 // so that we can discard update requests coming from polishEvent().
3508 d->scene->d_func()->updateAll = false;
3509
3510 // Find all exposed items
3511 bool allItems = false;
3512 QList<QGraphicsItem *> itemList = d->findItems(exposedRegion: d->exposedRegion, allItems: &allItems, viewTransform);
3513 if (!itemList.isEmpty()) {
3514 // Generate the style options.
3515 const int numItems = itemList.size();
3516 QGraphicsItem **itemArray = &itemList[0]; // Relies on QList internals, but is perfectly valid.
3517 QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
3518 QTransform transform(Qt::Uninitialized);
3519 for (int i = 0; i < numItems; ++i) {
3520 QGraphicsItem *item = itemArray[i];
3521 QGraphicsItemPrivate *itemd = item->d_ptr.data();
3522 itemd->initStyleOption(option: &styleOptionArray[i], worldTransform: viewTransform, exposedRegion: d->exposedRegion, allItems);
3523 // Cache the item's area in view coordinates.
3524 // Note that we have to do this here in case the base class implementation
3525 // (QGraphicsScene::drawItems) is not called. If it is, we'll do this
3526 // operation twice, but that's the price one has to pay for using indirect
3527 // painting :-/.
3528 const QRectF brect = adjustedItemEffectiveBoundingRect(item);
3529 if (!itemd->itemIsUntransformable()) {
3530 transform = item->sceneTransform();
3531 if (viewTransformed)
3532 transform *= viewTransform;
3533 } else {
3534 transform = item->deviceTransform(viewportTransform: viewTransform);
3535 }
3536 itemd->paintedViewBoundingRects.insert(key: d->viewport, value: transform.mapRect(brect).toRect());
3537 }
3538 // Draw the items.
3539 drawItems(painter: &painter, numItems, items: itemArray, options: styleOptionArray);
3540 d->freeStyleOptionsArray(array: styleOptionArray);
3541 }
3542 }
3543
3544 // Foreground
3545 drawForeground(painter: &painter, rect: exposedSceneRect);
3546
3547 #if QT_CONFIG(rubberband)
3548 // Rubberband
3549 if (d->rubberBanding && !d->rubberBandRect.isEmpty()) {
3550 painter.restore();
3551 QStyleOptionRubberBand option;
3552 option.initFrom(w: viewport());
3553 option.rect = d->rubberBandRect;
3554 option.shape = QRubberBand::Rectangle;
3555
3556 QStyleHintReturnMask mask;
3557 if (viewport()->style()->styleHint(stylehint: QStyle::SH_RubberBand_Mask, opt: &option, widget: viewport(), returnData: &mask)) {
3558 // painter clipping for masked rubberbands
3559 painter.setClipRegion(mask.region, op: Qt::IntersectClip);
3560 }
3561
3562 viewport()->style()->drawControl(element: QStyle::CE_RubberBand, opt: &option, p: &painter, w: viewport());
3563 }
3564 #endif
3565 };
3566
3567 actuallyDraw();
3568
3569 // For stereo we want to draw everything twice, once to each buffer
3570 if (d->stereoEnabled) {
3571 QWidgetPrivate* w = QWidgetPrivate::get(w: viewport());
3572 if (w->toggleStereoTargetBuffer()) {
3573 actuallyDraw();
3574 w->toggleStereoTargetBuffer();
3575 }
3576 }
3577
3578 painter.end();
3579
3580 // Restore painter state protection.
3581 d->scene->d_func()->painterStateProtection = true;
3582}
3583
3584/*!
3585 \reimp
3586*/
3587void QGraphicsView::resizeEvent(QResizeEvent *event)
3588{
3589 Q_D(QGraphicsView);
3590 // Save the last center point - the resize may scroll the view, which
3591 // changes the center point.
3592 QPointF oldLastCenterPoint = d->lastCenterPoint;
3593
3594 QAbstractScrollArea::resizeEvent(event);
3595 d->recalculateContentSize();
3596
3597 // Restore the center point again.
3598 if (d->resizeAnchor == NoAnchor && !d->keepLastCenterPoint) {
3599 d->updateLastCenterPoint();
3600 } else {
3601 d->lastCenterPoint = oldLastCenterPoint;
3602 }
3603 d->centerView(anchor: d->resizeAnchor);
3604 d->keepLastCenterPoint = false;
3605
3606 if (d->cacheMode & CacheBackground) {
3607 // Invalidate the background pixmap
3608 d->mustResizeBackgroundPixmap = true;
3609 }
3610}
3611
3612/*!
3613 \reimp
3614*/
3615void QGraphicsView::scrollContentsBy(int dx, int dy)
3616{
3617 Q_D(QGraphicsView);
3618 d->dirtyScroll = true;
3619 if (d->transforming)
3620 return;
3621 if (isRightToLeft())
3622 dx = -dx;
3623
3624 if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate) {
3625 if (d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) {
3626 if (d->accelerateScrolling) {
3627#if QT_CONFIG(rubberband)
3628 // Update new and old rubberband regions
3629 if (!d->rubberBandRect.isEmpty()) {
3630 QRegion rubberBandRegion(d->rubberBandRegion(widget: viewport(), rect: d->rubberBandRect));
3631 rubberBandRegion += rubberBandRegion.translated(dx: -dx, dy: -dy);
3632 viewport()->update(rubberBandRegion);
3633 }
3634#endif
3635 d->dirtyScrollOffset.rx() += dx;
3636 d->dirtyScrollOffset.ry() += dy;
3637 d->dirtyRegion.translate(dx, dy);
3638 viewport()->scroll(dx, dy);
3639 } else {
3640 d->updateAll();
3641 }
3642 } else {
3643 d->updateAll();
3644 }
3645 }
3646
3647 d->updateLastCenterPoint();
3648
3649 if (d->cacheMode & CacheBackground) {
3650 // Below, QPixmap::scroll() works in device pixels, while the delta values
3651 // and backgroundPixmapExposed are in device independent pixels.
3652 const qreal dpr = d->backgroundPixmap.devicePixelRatio();
3653 const qreal inverseDpr = qreal(1) / dpr;
3654
3655 // Scroll the background pixmap
3656 QRegion exposed;
3657 if (!d->backgroundPixmap.isNull())
3658 d->backgroundPixmap.scroll(dx: dx * dpr, dy: dy * dpr, rect: d->backgroundPixmap.rect(), exposed: &exposed);
3659
3660 // Invalidate the background pixmap
3661 d->backgroundPixmapExposed.translate(dx, dy);
3662 const QRegion exposedScaled = QTransform::fromScale(dx: inverseDpr, dy: inverseDpr).map(r: exposed);
3663 d->backgroundPixmapExposed += exposedScaled;
3664 }
3665
3666 // Always replay on scroll.
3667 if (d->sceneInteractionAllowed)
3668 d->replayLastMouseEvent();
3669}
3670
3671/*!
3672 \reimp
3673*/
3674void QGraphicsView::showEvent(QShowEvent *event)
3675{
3676 Q_D(QGraphicsView);
3677 d->recalculateContentSize();
3678 d->centerView(anchor: d->transformationAnchor);
3679 QAbstractScrollArea::showEvent(event);
3680}
3681
3682/*!
3683 \reimp
3684*/
3685void QGraphicsView::inputMethodEvent(QInputMethodEvent *event)
3686{
3687 Q_D(QGraphicsView);
3688 if (d->scene)
3689 QCoreApplication::sendEvent(receiver: d->scene, event);
3690}
3691
3692/*!
3693 Draws the background of the scene using \a painter, before any items and
3694 the foreground are drawn. Reimplement this function to provide a custom
3695 background for this view.
3696
3697 If all you want is to define a color, texture or gradient for the
3698 background, you can call setBackgroundBrush() instead.
3699
3700 All painting is done in \e scene coordinates. \a rect is the exposed
3701 rectangle.
3702
3703 The default implementation fills \a rect using the view's backgroundBrush.
3704 If no such brush is defined (the default), the scene's drawBackground()
3705 function is called instead.
3706
3707 \sa drawForeground(), QGraphicsScene::drawBackground()
3708*/
3709void QGraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
3710{
3711 Q_D(QGraphicsView);
3712 if (d->scene && d->backgroundBrush.style() == Qt::NoBrush) {
3713 d->scene->drawBackground(painter, rect);
3714 return;
3715 }
3716
3717 const bool wasAa = painter->testRenderHint(hint: QPainter::Antialiasing);
3718 if (wasAa)
3719 painter->setRenderHints(hints: QPainter::Antialiasing, on: false);
3720 painter->fillRect(rect, d->backgroundBrush);
3721 if (wasAa)
3722 painter->setRenderHints(hints: QPainter::Antialiasing, on: true);
3723}
3724
3725/*!
3726 Draws the foreground of the scene using \a painter, after the background
3727 and all items are drawn. Reimplement this function to provide a custom
3728 foreground for this view.
3729
3730 If all you want is to define a color, texture or gradient for the
3731 foreground, you can call setForegroundBrush() instead.
3732
3733 All painting is done in \e scene coordinates. \a rect is the exposed
3734 rectangle.
3735
3736 The default implementation fills \a rect using the view's foregroundBrush.
3737 If no such brush is defined (the default), the scene's drawForeground()
3738 function is called instead.
3739
3740 \sa drawBackground(), QGraphicsScene::drawForeground()
3741*/
3742void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
3743{
3744 Q_D(QGraphicsView);
3745 if (d->scene && d->foregroundBrush.style() == Qt::NoBrush) {
3746 d->scene->drawForeground(painter, rect);
3747 return;
3748 }
3749
3750 painter->fillRect(rect, d->foregroundBrush);
3751}
3752
3753/*!
3754 \deprecated
3755
3756 Draws the items \a items in the scene using \a painter, after the
3757 background and before the foreground are drawn. \a numItems is the number
3758 of items in \a items and options in \a options. \a options is a list of
3759 styleoptions; one for each item. Reimplement this function to provide
3760 custom item drawing for this view.
3761
3762 The default implementation calls the scene's drawItems() function.
3763
3764 Since Qt 4.6, this function is not called anymore unless
3765 the QGraphicsView::IndirectPainting flag is given as an Optimization
3766 flag.
3767
3768 \sa drawForeground(), drawBackground(), QGraphicsScene::drawItems()
3769*/
3770void QGraphicsView::drawItems(QPainter *painter, int numItems,
3771 QGraphicsItem *items[],
3772 const QStyleOptionGraphicsItem options[])
3773{
3774 Q_D(QGraphicsView);
3775 if (d->scene) {
3776 QWidget *widget = painter->device() == viewport() ? viewport() : nullptr;
3777 d->scene->drawItems(painter, numItems, items, options, widget);
3778 }
3779}
3780
3781/*!
3782 Returns the current transformation matrix for the view. If no current
3783 transformation is set, the identity matrix is returned.
3784
3785 \sa setTransform(), rotate(), scale(), shear(), translate()
3786*/
3787QTransform QGraphicsView::transform() const
3788{
3789 Q_D(const QGraphicsView);
3790 return d->matrix;
3791}
3792
3793/*!
3794 Returns a matrix that maps scene coordinates to viewport coordinates.
3795
3796 \sa mapToScene(), mapFromScene()
3797*/
3798QTransform QGraphicsView::viewportTransform() const
3799{
3800 Q_D(const QGraphicsView);
3801 QTransform moveMatrix = QTransform::fromTranslate(dx: -d->horizontalScroll(), dy: -d->verticalScroll());
3802 return d->identityMatrix ? moveMatrix : d->matrix * moveMatrix;
3803}
3804
3805/*!
3806 \since 4.6
3807
3808 Returns \c true if the view is transformed (i.e., a non-identity transform
3809 has been assigned, or the scrollbars are adjusted).
3810
3811 \sa setTransform(), horizontalScrollBar(), verticalScrollBar()
3812*/
3813bool QGraphicsView::isTransformed() const
3814{
3815 Q_D(const QGraphicsView);
3816 return !d->identityMatrix || d->horizontalScroll() || d->verticalScroll();
3817}
3818
3819/*!
3820 Sets the view's current transformation matrix to \a matrix.
3821
3822 If \a combine is true, then \a matrix is combined with the current matrix;
3823 otherwise, \a matrix \e replaces the current matrix. \a combine is false
3824 by default.
3825
3826 The transformation matrix transforms the scene into view coordinates. Using
3827 the default transformation, provided by the identity matrix, one pixel in
3828 the view represents one unit in the scene (e.g., a 10x10 rectangular item
3829 is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
3830 applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
3831 then drawn using 20x20 pixels in the view).
3832
3833 Example:
3834
3835 \snippet code/src_gui_graphicsview_qgraphicsview.cpp 7
3836
3837 To simplify interaction with items using a transformed view, QGraphicsView
3838 provides mapTo... and mapFrom... functions that can translate between
3839 scene and view coordinates. For example, you can call mapToScene() to map
3840 a view coordinate to a floating point scene coordinate, or mapFromScene()
3841 to map from floating point scene coordinates to view coordinates.
3842
3843 \sa transform(), resetTransform(), rotate(), scale(), shear(), translate()
3844*/
3845void QGraphicsView::setTransform(const QTransform &matrix, bool combine )
3846{
3847 Q_D(QGraphicsView);
3848 QTransform oldMatrix = d->matrix;
3849 if (!combine)
3850 d->matrix = matrix;
3851 else
3852 d->matrix = matrix * d->matrix;
3853 if (oldMatrix == d->matrix)
3854 return;
3855
3856 d->identityMatrix = d->matrix.isIdentity();
3857 d->transforming = true;
3858 if (d->scene) {
3859 d->recalculateContentSize();
3860 d->centerView(anchor: d->transformationAnchor);
3861 } else {
3862 d->updateLastCenterPoint();
3863 }
3864
3865 if (d->sceneInteractionAllowed)
3866 d->replayLastMouseEvent();
3867 d->transforming = false;
3868
3869 // Any matrix operation requires a full update.
3870 d->updateAll();
3871}
3872
3873/*!
3874 Resets the view transformation to the identity matrix.
3875
3876 \sa transform(), setTransform()
3877*/
3878void QGraphicsView::resetTransform()
3879{
3880 setTransform(matrix: QTransform());
3881}
3882
3883QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const
3884{
3885 QPointF p = point;
3886 p.rx() += horizontalScroll();
3887 p.ry() += verticalScroll();
3888 return identityMatrix ? p : matrix.inverted().map(p);
3889}
3890
3891QRectF QGraphicsViewPrivate::mapToScene(const QRectF &rect) const
3892{
3893 QPointF scrollOffset(horizontalScroll(), verticalScroll());
3894 QPointF tl = scrollOffset + rect.topLeft();
3895 QPointF tr = scrollOffset + rect.topRight();
3896 QPointF br = scrollOffset + rect.bottomRight();
3897 QPointF bl = scrollOffset + rect.bottomLeft();
3898
3899 QPolygonF poly(4);
3900 if (!identityMatrix) {
3901 QTransform x = matrix.inverted();
3902 poly[0] = x.map(p: tl);
3903 poly[1] = x.map(p: tr);
3904 poly[2] = x.map(p: br);
3905 poly[3] = x.map(p: bl);
3906 } else {
3907 poly[0] = tl;
3908 poly[1] = tr;
3909 poly[2] = br;
3910 poly[3] = bl;
3911 }
3912 return poly.boundingRect();
3913}
3914
3915QT_END_NAMESPACE
3916
3917#include "moc_qgraphicsview.cpp"
3918

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/widgets/graphicsview/qgraphicsview.cpp