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

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