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