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

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