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