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

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