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
qgraphicsscene.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5/*!
6 \class QGraphicsScene
7 \brief The QGraphicsScene class provides a surface for managing a large
8 number of 2D graphical items.
9 \since 4.2
10 \ingroup graphicsview-api
11 \inmodule QtWidgets
12
13 The class serves as a container for QGraphicsItems. It is used together
14 with QGraphicsView for visualizing graphical items, such as lines,
15 rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is
16 part of the \l{Graphics View Framework}.
17
18 QGraphicsScene also provides functionality that lets you efficiently
19 determine both the location of items, and for determining what items are
20 visible within an arbitrary area on the scene. With the QGraphicsView
21 widget, you can either visualize the whole scene, or zoom in and view only
22 parts of the scene.
23
24 Example:
25
26 \snippet code/src_gui_graphicsview_qgraphicsscene.cpp 0
27
28 Note that QGraphicsScene has no visual appearance of its own; it only
29 manages the items. You need to create a QGraphicsView widget to visualize
30 the scene.
31
32 To add items to a scene, you start off by constructing a QGraphicsScene
33 object. Then, you have two options: either add your existing QGraphicsItem
34 objects by calling addItem(), or you can call one of the convenience
35 functions addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(),
36 addRect(), or addText(), which all return a pointer to the newly added item.
37 The dimensions of the items added with these functions are relative to the
38 item's coordinate system, and the items position is initialized to (0,
39 0) in the scene.
40
41 You can then visualize the scene using QGraphicsView. When the scene
42 changes, (e.g., when an item moves or is transformed) QGraphicsScene
43 emits the changed() signal. To remove an item, call removeItem().
44
45 QGraphicsScene uses an indexing algorithm to manage the location of items
46 efficiently. By default, a BSP (Binary Space Partitioning) tree is used; an
47 algorithm suitable for large scenes where most items remain static (i.e.,
48 do not move around). You can choose to disable this index by calling
49 setItemIndexMethod(). For more information about the available indexing
50 algorithms, see the itemIndexMethod property.
51
52 The scene's bounding rect is set by calling setSceneRect(). Items can be
53 placed at any position on the scene, and the size of the scene is by
54 default unlimited. The scene rect is used only for internal bookkeeping,
55 maintaining the scene's item index. If the scene rect is unset,
56 QGraphicsScene will use the bounding area of all items, as returned by
57 itemsBoundingRect(), as the scene rect. However, itemsBoundingRect() is a
58 relatively time consuming function, as it operates by collecting
59 positional information for every item on the scene. Because of this, you
60 should always set the scene rect when operating on large scenes.
61
62 One of QGraphicsScene's greatest strengths is its ability to efficiently
63 determine the location of items. Even with millions of items on the scene,
64 the items() functions can determine the location of an item within a few
65 milliseconds. There are several overloads to items(): one that finds items
66 at a certain position, one that finds items inside or intersecting with a
67 polygon or a rectangle, and more. The list of returned items is sorted by
68 stacking order, with the topmost item being the first item in the list.
69 For convenience, there is also an itemAt() function that returns the
70 topmost item at a given position.
71
72 QGraphicsScene maintains selection information for the scene. To select
73 items, call setSelectionArea(), and to clear the current selection, call
74 clearSelection(). Call selectedItems() to get the list of all selected
75 items.
76
77 \section1 Event Handling and Propagation
78
79 Another responsibility that QGraphicsScene has, is to propagate events
80 from QGraphicsView. To send an event to a scene, you construct an event
81 that inherits QEvent, and then send it using, for example,
82 QCoreApplication::sendEvent(). event() is responsible for dispatching
83 the event to the individual items. Some common events are handled by
84 convenience event handlers. For example, key press events are handled by
85 keyPressEvent(), and mouse press events are handled by mousePressEvent().
86
87 Key events are delivered to the \e {focus item}. To set the focus item,
88 you can either call setFocusItem(), passing an item that accepts focus, or
89 the item itself can call QGraphicsItem::setFocus(). Call focusItem() to
90 get the current focus item. For compatibility with widgets, the scene also
91 maintains its own focus information. By default, the scene does not have
92 focus, and all key events are discarded. If setFocus() is called, or if an
93 item on the scene gains focus, the scene automatically gains focus. If the
94 scene has focus, hasFocus() will return true, and key events will be
95 forwarded to the focus item, if any. If the scene loses focus, (i.e.,
96 someone calls clearFocus()) while an item has focus, the scene will
97 maintain its item focus information, and once the scene regains focus, it
98 will make sure the last focus item regains focus.
99
100 For mouse-over effects, QGraphicsScene dispatches \e {hover
101 events}. If an item accepts hover events (see
102 QGraphicsItem::acceptHoverEvents()), it will receive a \l
103 {QEvent::}{GraphicsSceneHoverEnter} event when the mouse enters
104 its area. As the mouse continues moving inside the item's area,
105 QGraphicsScene will send it \l {QEvent::}{GraphicsSceneHoverMove}
106 events. When the mouse leaves the item's area, the item will
107 receive a \l {QEvent::}{GraphicsSceneHoverLeave} event.
108
109 All mouse events are delivered to the current \e {mouse grabber}
110 item. An item becomes the scene's mouse grabber if it accepts
111 mouse events (see QGraphicsItem::acceptedMouseButtons()) and it
112 receives a mouse press. It stays the mouse grabber until it
113 receives a mouse release when no other mouse buttons are
114 pressed. You can call mouseGrabberItem() to determine what item is
115 currently grabbing the mouse.
116
117 \sa QGraphicsItem, QGraphicsView
118*/
119
120/*!
121 \enum QGraphicsScene::SceneLayer
122 \since 4.3
123
124 This enum describes the rendering layers in a QGraphicsScene. When
125 QGraphicsScene draws the scene contents, it renders each of these layers
126 separately, in order.
127
128 Each layer represents a flag that can be OR'ed together when calling
129 functions such as invalidate() or QGraphicsView::invalidateScene().
130
131 \value ItemLayer The item layer. QGraphicsScene renders all items are in
132 this layer by calling the virtual function drawItems(). The item layer is
133 drawn after the background layer, but before the foreground layer.
134
135 \value BackgroundLayer The background layer. QGraphicsScene renders the
136 scene's background in this layer by calling the virtual function
137 drawBackground(). The background layer is drawn first of all layers.
138
139 \value ForegroundLayer The foreground layer. QGraphicsScene renders the
140 scene's foreground in this layer by calling the virtual function
141 drawForeground(). The foreground layer is drawn last of all layers.
142
143 \value AllLayers All layers; this value represents a combination of all
144 three layers.
145
146 \sa invalidate(), QGraphicsView::invalidateScene()
147*/
148
149/*!
150 \enum QGraphicsScene::ItemIndexMethod
151
152 This enum describes the indexing algorithms QGraphicsScene provides for
153 managing positional information about items on the scene.
154
155 \value BspTreeIndex A Binary Space Partitioning tree is applied. All
156 QGraphicsScene's item location algorithms are of an order close to
157 logarithmic complexity, by making use of binary search. Adding, moving and
158 removing items is logarithmic. This approach is best for static scenes
159 (i.e., scenes where most items do not move).
160
161 \value NoIndex No index is applied. Item location is of linear complexity,
162 as all items on the scene are searched. Adding, moving and removing items,
163 however, is done in constant time. This approach is ideal for dynamic
164 scenes, where many items are added, moved or removed continuously.
165
166 \sa setItemIndexMethod(), bspTreeDepth
167*/
168
169#include "qgraphicsscene.h"
170
171#include "qgraphicsitem.h"
172#include "qgraphicsitem_p.h"
173#include "qgraphicslayout.h"
176#include "qgraphicsview.h"
177#include "qgraphicsview_p.h"
178#include "qgraphicswidget.h"
183
184#include <QtCore/qdebug.h>
185#include <QtCore/qlist.h>
186#include <QtCore/qmath.h>
187#include <QtCore/qrect.h>
188#include <QtCore/qset.h>
189#include <QtCore/qstack.h>
190#include <QtCore/qvarlengtharray.h>
191#include <QtCore/QMetaMethod>
192#include <QtWidgets/qapplication.h>
193#include <QtGui/qevent.h>
194#include <QtWidgets/qgraphicslayout.h>
195#include <QtWidgets/qgraphicsproxywidget.h>
196#include <QtWidgets/qgraphicswidget.h>
197#include <QtGui/qpaintengine.h>
198#include <QtGui/qpainter.h>
199#include <QtGui/qpainterpath.h>
200#include <QtGui/qpixmapcache.h>
201#include <QtGui/qpolygon.h>
202#include <QtGui/qpointingdevice.h>
203#include <QtWidgets/qstyleoption.h>
204#if QT_CONFIG(tooltip)
205#include <QtWidgets/qtooltip.h>
206#endif
207#include <QtGui/qtransform.h>
208#include <QtGui/qinputmethod.h>
209#include <private/qapplication_p.h>
210#include <private/qevent_p.h>
211#include <QtGui/private/qeventpoint_p.h>
212#include <private/qobject_p.h>
213#if QT_CONFIG(graphicseffect)
214#include <private/qgraphicseffect_p.h>
215#endif
216#include <private/qgesturemanager_p.h>
217#include <private/qpathclipper_p.h>
218
219#include <QtCore/qpointer.h>
220
221// #define GESTURE_DEBUG
222#ifndef GESTURE_DEBUG
223# define DEBUG if (0) qDebug
224#else
225# define DEBUG qDebug
226#endif
227
229
230bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
231
232static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
233{
234 hover->setWidget(mouseEvent->widget());
235 hover->setPos(mouseEvent->pos());
236 hover->setScenePos(mouseEvent->scenePos());
237 hover->setScreenPos(mouseEvent->screenPos());
238 hover->setLastPos(mouseEvent->lastPos());
239 hover->setLastScenePos(mouseEvent->lastScenePos());
240 hover->setLastScreenPos(mouseEvent->lastScreenPos());
241 hover->setModifiers(mouseEvent->modifiers());
242 hover->setAccepted(mouseEvent->isAccepted());
243}
244
245/*!
246 \internal
247*/
248QGraphicsScenePrivate::QGraphicsScenePrivate()
249 : indexMethod(QGraphicsScene::BspTreeIndex),
250 index(nullptr),
251 lastItemCount(0),
252 hasSceneRect(false),
253 dirtyGrowingItemsBoundingRect(true),
254 updateAll(false),
255 calledEmitUpdated(false),
256 processDirtyItemsEmitted(false),
257 needSortTopLevelItems(true),
258 holesInTopLevelSiblingIndex(false),
259 topLevelSequentialOrdering(true),
260 scenePosDescendantsUpdatePending(false),
261 stickyFocus(false),
262 hasFocus(false),
263 lastMouseGrabberItemHasImplicitMouseGrab(false),
264 allItemsIgnoreHoverEvents(true),
265 allItemsUseDefaultCursor(true),
266 painterStateProtection(true),
267 sortCacheEnabled(false),
268 allItemsIgnoreTouchEvents(true),
269 focusOnTouch(true),
270 minimumRenderSize(0.0),
271 selectionChanging(0),
272 rectAdjust(2),
273 focusItem(nullptr),
274 lastFocusItem(nullptr),
275 passiveFocusItem(nullptr),
276 tabFocusFirst(nullptr),
277 activePanel(nullptr),
278 lastActivePanel(nullptr),
279 activationRefCount(0),
280 childExplicitActivation(0),
281 lastMouseGrabberItem(nullptr),
282 dragDropItem(nullptr),
283 enterWidget(nullptr),
284 lastDropAction(Qt::IgnoreAction),
285 style(nullptr)
286{
287}
288
289/*!
290 \internal
291*/
292void QGraphicsScenePrivate::init()
293{
294 Q_Q(QGraphicsScene);
295
296 index = new QGraphicsSceneBspTreeIndex(q);
297
298 // Keep this index so we can check for connected slots later on.
299 changedSignalIndex = signalIndex("changed(QList<QRectF>)");
300 processDirtyItemsIndex = q->metaObject()->indexOfSlot("_q_processDirtyItems()");
301 polishItemsIndex = q->metaObject()->indexOfSlot("_q_polishItems()");
302
303 qApp->d_func()->scene_list.append(q);
304 q->update();
305}
306
307/*!
308 \internal
309*/
310QGraphicsScenePrivate *QGraphicsScenePrivate::get(QGraphicsScene *q)
311{
312 return q->d_func();
313}
314
315void QGraphicsScenePrivate::_q_emitUpdated()
316{
317 Q_Q(QGraphicsScene);
318 calledEmitUpdated = false;
319
320 if (dirtyGrowingItemsBoundingRect) {
321 if (!hasSceneRect) {
322 const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
323 growingItemsBoundingRect |= q->itemsBoundingRect();
324 if (oldGrowingItemsBoundingRect != growingItemsBoundingRect)
325 emit q->sceneRectChanged(growingItemsBoundingRect);
326 }
327 dirtyGrowingItemsBoundingRect = false;
328 }
329
330 // Ensure all views are connected if anything is connected. This disables
331 // the optimization that items send updates directly to the views, but it
332 // needs to happen in order to keep compatibility with the behavior from
333 // Qt 4.4 and backward.
334 if (isSignalConnected(changedSignalIndex)) {
335 for (auto view : std::as_const(views)) {
336 if (!view->d_func()->connectedToScene) {
337 view->d_func()->connectedToScene = true;
338 q->connect(q, SIGNAL(changed(QList<QRectF>)),
339 view, SLOT(updateScene(QList<QRectF>)));
340 }
341 }
342 } else {
343 if (views.isEmpty()) {
344 updateAll = false;
345 return;
346 }
347 for (auto view : std::as_const(views))
348 view->d_func()->processPendingUpdates();
349 // It's important that we update all views before we dispatch, hence two for-loops.
350 for (auto view : std::as_const(views))
351 view->d_func()->dispatchPendingUpdateRequests();
352 return;
353 }
354
355 // Notify the changes to anybody interested.
356 QList<QRectF> oldUpdatedRects;
357 if (updateAll) {
358 oldUpdatedRects << q->sceneRect();
359 } else {
360 // Switch to a ranged constructor in Qt 6...
361 oldUpdatedRects.reserve(int(updatedRects.size()));
362 std::copy(updatedRects.cbegin(), updatedRects.cend(),
363 std::back_inserter(oldUpdatedRects));
364 }
365
366 updateAll = false;
367 updatedRects.clear();
368 emit q->changed(oldUpdatedRects);
369}
370
371/*!
372 \internal
373
374 ### This function is almost identical to QGraphicsItemPrivate::addChild().
375*/
376void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item)
377{
378 ensureSequentialTopLevelSiblingIndexes();
379 needSortTopLevelItems = true; // ### maybe false
380 item->d_ptr->siblingIndex = topLevelItems.size();
381 topLevelItems.append(item);
382}
383
384/*!
385 \internal
386
387 ### This function is almost identical to QGraphicsItemPrivate::removeChild().
388*/
389void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item)
390{
391 if (!holesInTopLevelSiblingIndex)
392 holesInTopLevelSiblingIndex = item->d_ptr->siblingIndex != topLevelItems.size() - 1;
393 if (topLevelSequentialOrdering && !holesInTopLevelSiblingIndex)
394 topLevelItems.removeAt(item->d_ptr->siblingIndex);
395 else
396 topLevelItems.removeOne(item);
397 // NB! Do not use topLevelItems.removeAt(item->d_ptr->siblingIndex) because
398 // the item is not guaranteed to be at the index after the list is sorted
399 // (see ensureSortedTopLevelItems()).
400 item->d_ptr->siblingIndex = -1;
401 if (topLevelSequentialOrdering)
402 topLevelSequentialOrdering = !holesInTopLevelSiblingIndex;
403}
404
405/*!
406 \internal
407*/
408void QGraphicsScenePrivate::_q_polishItems()
409{
410 if (unpolishedItems.isEmpty())
411 return;
412
413 const QVariant booleanTrueVariant(true);
414 QGraphicsItem *item = nullptr;
415 QGraphicsItemPrivate *itemd = nullptr;
416 const int oldUnpolishedCount = unpolishedItems.size();
417
418 for (int i = 0; i < oldUnpolishedCount; ++i) {
419 item = unpolishedItems.at(i);
420 if (!item)
421 continue;
422 itemd = item->d_ptr.data();
423 itemd->pendingPolish = false;
424 if (!itemd->explicitlyHidden) {
425 item->itemChange(QGraphicsItem::ItemVisibleChange, booleanTrueVariant);
426 item->itemChange(QGraphicsItem::ItemVisibleHasChanged, booleanTrueVariant);
427 }
428 if (itemd->isWidget) {
429 QEvent event(QEvent::Polish);
430 QCoreApplication::sendEvent((QGraphicsWidget *)item, &event);
431 }
432 }
433
434 if (unpolishedItems.size() == oldUnpolishedCount) {
435 // No new items were added to the vector.
436 unpolishedItems.clear();
437 } else {
438 // New items were appended; keep them and remove the old ones.
439 unpolishedItems.remove(0, oldUnpolishedCount);
440 unpolishedItems.squeeze();
441 QMetaObject::invokeMethod(q_ptr, "_q_polishItems", Qt::QueuedConnection);
442 }
443}
444
445void QGraphicsScenePrivate::_q_processDirtyItems()
446{
447 processDirtyItemsEmitted = false;
448
449 if (updateAll) {
450 Q_ASSERT(calledEmitUpdated);
451 // No need for further processing (except resetting the dirty states).
452 // The growingItemsBoundingRect is updated in _q_emitUpdated.
453 for (auto topLevelItem : std::as_const(topLevelItems))
454 resetDirtyItem(topLevelItem, /*recursive=*/true);
455 return;
456 }
457
458 const bool wasPendingSceneUpdate = calledEmitUpdated;
459 const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
460
461 // Process items recursively.
462 for (auto topLevelItem : std::as_const(topLevelItems))
463 processDirtyItemsRecursive(topLevelItem);
464
465 dirtyGrowingItemsBoundingRect = false;
466 if (!hasSceneRect && oldGrowingItemsBoundingRect != growingItemsBoundingRect)
467 emit q_func()->sceneRectChanged(growingItemsBoundingRect);
468
469 if (wasPendingSceneUpdate)
470 return;
471
472 for (auto view : std::as_const(views))
473 view->d_func()->processPendingUpdates();
474
475 if (calledEmitUpdated) {
476 // We did a compatibility QGraphicsScene::update in processDirtyItemsRecursive
477 // and we cannot wait for the control to reach the eventloop before the
478 // changed signal is emitted, so we emit it now.
479 _q_emitUpdated();
480 }
481
482 // Immediately dispatch all pending update requests on the views.
483 for (auto view : std::as_const(views))
484 view->d_func()->dispatchPendingUpdateRequests();
485}
486
487/*!
488 \internal
489*/
490void QGraphicsScenePrivate::setScenePosItemEnabled(QGraphicsItem *item, bool enabled)
491{
492 QGraphicsItem *p = item->d_ptr->parent;
493 while (p) {
494 p->d_ptr->scenePosDescendants = enabled;
495 p = p->d_ptr->parent;
496 }
497 if (!enabled && !scenePosDescendantsUpdatePending) {
498 scenePosDescendantsUpdatePending = true;
499 QMetaObject::invokeMethod(q_func(), "_q_updateScenePosDescendants", Qt::QueuedConnection);
500 }
501}
502
503/*!
504 \internal
505*/
506void QGraphicsScenePrivate::registerScenePosItem(QGraphicsItem *item)
507{
508 scenePosItems.insert(item);
509 setScenePosItemEnabled(item, true);
510}
511
512/*!
513 \internal
514*/
515void QGraphicsScenePrivate::unregisterScenePosItem(QGraphicsItem *item)
516{
517 scenePosItems.remove(item);
518 setScenePosItemEnabled(item, false);
519}
520
521/*!
522 \internal
523*/
524void QGraphicsScenePrivate::_q_updateScenePosDescendants()
525{
526 for (QGraphicsItem *item : std::as_const(scenePosItems)) {
527 QGraphicsItem *p = item->d_ptr->parent;
528 while (p) {
529 p->d_ptr->scenePosDescendants = 1;
530 p = p->d_ptr->parent;
531 }
532 }
533 scenePosDescendantsUpdatePending = false;
534}
535
536/*!
537 \internal
538
539 Schedules an item for removal. This function leaves some stale indexes
540 around in the BSP tree if called from the item's destructor; these will
541 be cleaned up the next time someone triggers purgeRemovedItems().
542
543 Note: This function might get called from QGraphicsItem's destructor. \a item is
544 being destroyed, so we cannot call any pure virtual functions on it (such
545 as boundingRect()). Also, it is unnecessary to update the item's own state
546 in any way.
547*/
548void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item)
549{
550 Q_Q(QGraphicsScene);
551
552 // Clear focus on the item to remove any reference in the focusWidget chain.
553 item->clearFocus();
554
555 markDirty(item, QRectF(), /*invalidateChildren=*/false, /*force=*/false,
556 /*ignoreOpacity=*/false, /*removingItemFromScene=*/true);
557
558 if (item->d_ptr->inDestructor) {
559 // The item is actually in its destructor, we call the special method in the index.
560 index->deleteItem(item);
561 } else {
562 // Can potentially call item->boundingRect() (virtual function), that's why
563 // we only can call this function if the item is not in its destructor.
564 index->removeItem(item);
565 }
566
567 item->d_ptr->clearSubFocus();
568
569 if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges)
570 unregisterScenePosItem(item);
571
572 QGraphicsScene *oldScene = item->d_func()->scene;
573 item->d_func()->scene = nullptr;
574
575 //We need to remove all children first because they might use their parent
576 //attributes (e.g. sceneTransform).
577 if (!item->d_ptr->inDestructor) {
578 // Remove all children recursively
579 for (auto child : std::as_const(item->d_ptr->children))
580 q->removeItem(child);
581 }
582
583 if (!item->d_ptr->inDestructor && !item->parentItem() && item->isWidget()) {
584 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
585 widget->d_func()->fixFocusChainBeforeReparenting(nullptr, oldScene, nullptr);
586 }
587
588 // Unregister focus proxy.
589 item->d_ptr->resetFocusProxy();
590
591 // Remove from parent, or unregister from toplevels.
592 if (QGraphicsItem *parentItem = item->parentItem()) {
593 if (parentItem->scene()) {
594 Q_ASSERT_X(parentItem->scene() == q, "QGraphicsScene::removeItem",
595 "Parent item's scene is different from this item's scene");
596 item->setParentItem(nullptr);
597 }
598 } else {
599 unregisterTopLevelItem(item);
600 }
601
602 // Reset the mouse grabber and focus item data.
603 if (item == focusItem)
604 focusItem = nullptr;
605 if (item == lastFocusItem)
606 lastFocusItem = nullptr;
607 if (item == passiveFocusItem)
608 passiveFocusItem = nullptr;
609 if (item == activePanel) {
610 // ### deactivate...
611 activePanel = nullptr;
612 }
613 if (item == lastActivePanel)
614 lastActivePanel = nullptr;
615
616 // Change tabFocusFirst to the next widget in focus chain if removing the current one.
617 if (item == tabFocusFirst) {
618 QGraphicsWidgetPrivate *wd = tabFocusFirst->d_func();
619 if (wd->focusNext && wd->focusNext != tabFocusFirst && wd->focusNext->scene() == q)
620 tabFocusFirst = wd->focusNext;
621 else
622 tabFocusFirst = nullptr;
623 }
624
625 // Cancel active touches
626 {
627 QMap<int, QGraphicsItem *>::iterator it = itemForTouchPointId.begin();
628 while (it != itemForTouchPointId.end()) {
629 if (it.value() == item) {
630 sceneCurrentTouchPoints.remove(it.key());
631 it = itemForTouchPointId.erase(it);
632 } else {
633 ++it;
634 }
635 }
636 }
637
638 // Disable selectionChanged() for individual items
639 ++selectionChanging;
640 int oldSelectedItemsSize = selectedItems.size();
641
642 // Update selected & hovered item bookkeeping
643 selectedItems.remove(item);
644 hoverItems.removeAll(item);
645 cachedItemsUnderMouse.removeAll(item);
646 if (item->d_ptr->pendingPolish) {
647 const int unpolishedIndex = unpolishedItems.indexOf(item);
648 if (unpolishedIndex != -1)
649 unpolishedItems[unpolishedIndex] = 0;
650 item->d_ptr->pendingPolish = false;
651 }
652 resetDirtyItem(item);
653
654 //We remove all references of item from the sceneEventFilter arrays
655 QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin();
656 while (iterator != sceneEventFilters.end()) {
657 if (iterator.value() == item || iterator.key() == item)
658 iterator = sceneEventFilters.erase(iterator);
659 else
660 ++iterator;
661 }
662
663 if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
664 leaveModal(item);
665
666 // Reset the mouse grabber and focus item data.
667 if (mouseGrabberItems.contains(item))
668 ungrabMouse(item, /* item is dying */ item->d_ptr->inDestructor);
669
670 // Reset the keyboard grabber
671 if (keyboardGrabberItems.contains(item))
672 ungrabKeyboard(item, /* item is dying */ item->d_ptr->inDestructor);
673
674 // Reset the last mouse grabber item
675 if (item == lastMouseGrabberItem)
676 lastMouseGrabberItem = nullptr;
677
678 // Reset the current drop item
679 if (item == dragDropItem)
680 dragDropItem = nullptr;
681
682 // Re-enable selectionChanged() for individual items
683 --selectionChanging;
684 if (!selectionChanging && selectedItems.size() != oldSelectedItemsSize)
685 emit q->selectionChanged();
686
687#ifndef QT_NO_GESTURES
688 for (auto it = gestureTargets.begin(); it != gestureTargets.end();) {
689 if (it.value() == item)
690 it = gestureTargets.erase(it);
691 else
692 ++it;
693 }
694
695 if (QGraphicsObject *dummy = item->toGraphicsObject()) {
696 cachedTargetItems.removeOne(dummy);
697 cachedItemGestures.remove(dummy);
698 cachedAlreadyDeliveredGestures.remove(dummy);
699 }
700
701 for (auto it = item->d_ptr->gestureContext.constBegin();
702 it != item->d_ptr->gestureContext.constEnd(); ++it)
703 ungrabGesture(item, it.key());
704#endif // QT_NO_GESTURES
705}
706
707/*!
708 \internal
709*/
710void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool duringActivationEvent)
711{
712 Q_Q(QGraphicsScene);
713 if (item && item->scene() != q) {
714 qWarning("QGraphicsScene::setActivePanel: item %p must be part of this scene",
715 item);
716 return;
717 }
718
719 // Ensure the scene has focus when we change panel activation.
720 q->setFocus(Qt::ActiveWindowFocusReason);
721
722 // Find the item's panel.
723 QGraphicsItem *panel = item ? item->panel() : nullptr;
724 lastActivePanel = panel ? activePanel : nullptr;
725 if (panel == activePanel || (!q->isActive() && !duringActivationEvent))
726 return;
727
728 QGraphicsItem *oldFocusItem = focusItem;
729
730 // Deactivate the last active panel.
731 if (activePanel) {
732 if (QGraphicsItem *fi = activePanel->focusItem()) {
733 // Remove focus from the current focus item.
734 if (fi == q->focusItem())
735 setFocusItemHelper(nullptr, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false);
736 }
737
738 QEvent event(QEvent::WindowDeactivate);
739 q->sendEvent(activePanel, &event);
740 } else if (panel && !duringActivationEvent) {
741 // Deactivate the scene if changing activation to a panel.
742 const auto items = q->items();
743 QEvent event(QEvent::WindowDeactivate);
744 for (QGraphicsItem *item : items) {
745 if (item->isVisible() && !item->isPanel() && !item->parentItem())
746 q->sendEvent(item, &event);
747 }
748 }
749
750 // Update activate state.
751 activePanel = panel;
752 QEvent event(QEvent::ActivationChange);
753 QCoreApplication::sendEvent(q, &event);
754
755 // Activate
756 if (panel) {
757 QEvent event(QEvent::WindowActivate);
758 q->sendEvent(panel, &event);
759
760 // Set focus on the panel's focus item, or itself if it's
761 // focusable, or on the first focusable item in the panel's
762 // focus chain as a last resort.
763 if (QGraphicsItem *focusItem = panel->focusItem()) {
764 setFocusItemHelper(focusItem, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false);
765 } else if (panel->flags() & QGraphicsItem::ItemIsFocusable) {
766 setFocusItemHelper(panel, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false);
767 } else if (panel->isWidget()) {
768 QGraphicsWidget *fw = static_cast<QGraphicsWidget *>(panel)->d_func()->focusNext;
769 do {
770 if (fw->focusPolicy() & Qt::TabFocus) {
771 setFocusItemHelper(fw, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false);
772 break;
773 }
774 fw = fw->d_func()->focusNext;
775 } while (fw != panel);
776 }
777 } else if (q->isActive()) {
778 const auto items = q->items();
779 // Activate the scene
780 QEvent event(QEvent::WindowActivate);
781 for (QGraphicsItem *item : items) {
782 if (item->isVisible() && !item->isPanel() && !item->parentItem())
783 q->sendEvent(item, &event);
784 }
785 }
786
787 emit q->focusItemChanged(focusItem, oldFocusItem, Qt::ActiveWindowFocusReason);
788}
789
790/*!
791 \internal
792
793 \a emitFocusChanged needs to be false when focus passes from one
794 item to another through setActivePanel(); i.e. when activation
795 passes from one panel to another, to avoid getting two focusChanged()
796 emissions; one focusChanged(0, lastFocus), then one
797 focusChanged(newFocus, 0). Instead setActivePanel() emits the signal
798 once itself: focusChanged(newFocus, oldFocus).
799*/
800void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
801 Qt::FocusReason focusReason,
802 bool emitFocusChanged)
803{
804 Q_Q(QGraphicsScene);
805 if (item == focusItem)
806 return;
807
808 // Clear focus if asked to set focus on something that can't
809 // accept input focus.
810 if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable)
811 || !item->isVisible() || !item->isEnabled())) {
812 item = nullptr;
813 }
814
815 // Set focus on the scene if an item requests focus.
816 if (item) {
817 q->setFocus(focusReason);
818 if (item == focusItem) {
819 if (emitFocusChanged)
820 emit q->focusItemChanged(focusItem, (QGraphicsItem *)nullptr, focusReason);
821 return;
822 }
823 }
824
825 QGraphicsItem *oldFocusItem = focusItem;
826 if (focusItem) {
827 lastFocusItem = focusItem;
828
829#ifndef QT_NO_IM
830 if (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod) {
831 // Close any external input method panel. This happens
832 // automatically by removing WA_InputMethodEnabled on
833 // the views, but if we are changing focus, we have to
834 // do it ourselves.
835 if (qApp)
836 QGuiApplication::inputMethod()->commit();
837 }
838#endif //QT_NO_IM
839
840 focusItem = nullptr;
841 QFocusEvent event(QEvent::FocusOut, focusReason);
842 sendEvent(lastFocusItem, &event);
843 }
844
845 // This handles the case that the item has been removed from the
846 // scene in response to the FocusOut event.
847 if (item && item->scene() != q)
848 item = nullptr;
849
850 if (item)
851 focusItem = item;
852 updateInputMethodSensitivityInViews();
853
854 if (item) {
855 QFocusEvent event(QEvent::FocusIn, focusReason);
856 sendEvent(item, &event);
857 }
858
859 if (emitFocusChanged)
860 emit q->focusItemChanged(focusItem, oldFocusItem, focusReason);
861}
862
863/*!
864 \internal
865*/
866void QGraphicsScenePrivate::addPopup(QGraphicsWidget *widget)
867{
868 Q_ASSERT(widget);
869 Q_ASSERT(!popupWidgets.contains(widget));
870 popupWidgets << widget;
871 if (QGraphicsWidget *focusWidget = widget->focusWidget()) {
872 focusWidget->setFocus(Qt::PopupFocusReason);
873 } else {
874 grabKeyboard((QGraphicsItem *)widget);
875 if (focusItem && popupWidgets.size() == 1) {
876 QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason);
877 sendEvent(focusItem, &event);
878 }
879 }
880 grabMouse((QGraphicsItem *)widget);
881}
882
883/*!
884 \internal
885
886 Remove \a widget from the popup list. Important notes:
887
888 \a widget is guaranteed to be in the list of popups, but it might not be
889 the last entry; you can hide any item in the pop list before the others,
890 and this must cause all later mouse grabbers to lose the grab.
891*/
892void QGraphicsScenePrivate::removePopup(QGraphicsWidget *widget, bool itemIsDying)
893{
894 Q_ASSERT(widget);
895 int index = popupWidgets.indexOf(widget);
896 Q_ASSERT(index != -1);
897
898 for (int i = popupWidgets.size() - 1; i >= index; --i) {
899 QGraphicsWidget *widget = popupWidgets.takeLast();
900 ungrabMouse(widget, itemIsDying);
901 if (focusItem && popupWidgets.isEmpty()) {
902 QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason);
903 sendEvent(focusItem, &event);
904 } else if (keyboardGrabberItems.contains(static_cast<QGraphicsItem *>(widget))) {
905 ungrabKeyboard(static_cast<QGraphicsItem *>(widget), itemIsDying);
906 }
907 if (!itemIsDying && widget->isVisible()) {
908 widget->QGraphicsItem::d_ptr->setVisibleHelper(false, /* explicit = */ false);
909 }
910 }
911}
912
913/*!
914 \internal
915*/
916void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit)
917{
918 // Append to list of mouse grabber items, and send a mouse grab event.
919 if (mouseGrabberItems.contains(item)) {
920 if (mouseGrabberItems.constLast() == item) {
921 Q_ASSERT(!implicit);
922 if (!lastMouseGrabberItemHasImplicitMouseGrab) {
923 qWarning("QGraphicsItem::grabMouse: already a mouse grabber");
924 } else {
925 // Upgrade to an explicit mouse grab
926 lastMouseGrabberItemHasImplicitMouseGrab = false;
927 }
928 } else {
929 qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p",
930 mouseGrabberItems.constLast());
931 }
932 return;
933 }
934
935 // Send ungrab event to the last grabber.
936 if (!mouseGrabberItems.isEmpty()) {
937 QGraphicsItem *last = mouseGrabberItems.constLast();
938 if (lastMouseGrabberItemHasImplicitMouseGrab) {
939 // Implicit mouse grab is immediately lost.
940 last->ungrabMouse();
941 } else {
942 // Just send ungrab event to current grabber.
943 QEvent ungrabEvent(QEvent::UngrabMouse);
944 sendEvent(last, &ungrabEvent);
945 }
946 }
947
948 mouseGrabberItems << item;
949 lastMouseGrabberItemHasImplicitMouseGrab = implicit;
950
951 // Send grab event to current grabber.
952 QEvent grabEvent(QEvent::GrabMouse);
953 sendEvent(item, &grabEvent);
954}
955
956/*!
957 \internal
958*/
959void QGraphicsScenePrivate::ungrabMouse(QGraphicsItem *item, bool itemIsDying)
960{
961 int index = mouseGrabberItems.indexOf(item);
962 if (index == -1) {
963 qWarning("QGraphicsItem::ungrabMouse: not a mouse grabber");
964 return;
965 }
966
967 if (item != mouseGrabberItems.constLast()) {
968 // Recursively ungrab the next mouse grabber until we reach this item
969 // to ensure state consistency.
970 ungrabMouse(mouseGrabberItems.at(index + 1), itemIsDying);
971 }
972 if (!popupWidgets.isEmpty() && item == popupWidgets.constLast()) {
973 // If the item is a popup, go via removePopup to ensure state
974 // consistency and that it gets hidden correctly - beware that
975 // removePopup() reenters this function to continue removing the grab.
976 removePopup(popupWidgets.constLast(), itemIsDying);
977 return;
978 }
979
980 // Send notification about mouse ungrab.
981 if (!itemIsDying) {
982 QEvent event(QEvent::UngrabMouse);
983 sendEvent(item, &event);
984 }
985
986 // Remove the item from the list of grabbers. Whenever this happens, we
987 // reset the implicitGrab (there can be only ever be one implicit grabber
988 // in a scene, and it is always the latest grabber; if the implicit grab
989 // is lost, it is not automatically regained.
990 mouseGrabberItems.takeLast();
991 lastMouseGrabberItemHasImplicitMouseGrab = false;
992
993 // Send notification about mouse regrab. ### It's unfortunate that all the
994 // items get a GrabMouse event, but this is a rare case with a simple
995 // implementation and it does ensure a consistent state.
996 if (!itemIsDying && !mouseGrabberItems.isEmpty()) {
997 QGraphicsItem *last = mouseGrabberItems.constLast();
998 QEvent event(QEvent::GrabMouse);
999 sendEvent(last, &event);
1000 }
1001}
1002
1003/*!
1004 \internal
1005*/
1006void QGraphicsScenePrivate::clearMouseGrabber()
1007{
1008 if (!mouseGrabberItems.isEmpty())
1009 mouseGrabberItems.constFirst()->ungrabMouse();
1010 lastMouseGrabberItem = nullptr;
1011}
1012
1013/*!
1014 \internal
1015*/
1016void QGraphicsScenePrivate::grabKeyboard(QGraphicsItem *item)
1017{
1018 if (keyboardGrabberItems.contains(item)) {
1019 if (keyboardGrabberItems.constLast() == item)
1020 qWarning("QGraphicsItem::grabKeyboard: already a keyboard grabber");
1021 else
1022 qWarning("QGraphicsItem::grabKeyboard: already blocked by keyboard grabber: %p",
1023 keyboardGrabberItems.constLast());
1024 return;
1025 }
1026
1027 // Send ungrab event to the last grabber.
1028 if (!keyboardGrabberItems.isEmpty()) {
1029 // Just send ungrab event to current grabber.
1030 QEvent ungrabEvent(QEvent::UngrabKeyboard);
1031 sendEvent(keyboardGrabberItems.constLast(), &ungrabEvent);
1032 }
1033
1034 keyboardGrabberItems << item;
1035
1036 // Send grab event to current grabber.
1037 QEvent grabEvent(QEvent::GrabKeyboard);
1038 sendEvent(item, &grabEvent);
1039}
1040
1041/*!
1042 \internal
1043*/
1044void QGraphicsScenePrivate::ungrabKeyboard(QGraphicsItem *item, bool itemIsDying)
1045{
1046 int index = keyboardGrabberItems.lastIndexOf(item);
1047 if (index == -1) {
1048 qWarning("QGraphicsItem::ungrabKeyboard: not a keyboard grabber");
1049 return;
1050 }
1051 if (item != keyboardGrabberItems.constLast()) {
1052 // Recursively ungrab the topmost keyboard grabber until we reach this
1053 // item to ensure state consistency.
1054 ungrabKeyboard(keyboardGrabberItems.at(index + 1), itemIsDying);
1055 }
1056
1057 // Send notification about keyboard ungrab.
1058 if (!itemIsDying) {
1059 QEvent event(QEvent::UngrabKeyboard);
1060 sendEvent(item, &event);
1061 }
1062
1063 // Remove the item from the list of grabbers.
1064 keyboardGrabberItems.takeLast();
1065
1066 // Send notification about mouse regrab.
1067 if (!itemIsDying && !keyboardGrabberItems.isEmpty()) {
1068 QGraphicsItem *last = keyboardGrabberItems.constLast();
1069 QEvent event(QEvent::GrabKeyboard);
1070 sendEvent(last, &event);
1071 }
1072}
1073
1074/*!
1075 \internal
1076*/
1077void QGraphicsScenePrivate::clearKeyboardGrabber()
1078{
1079 if (!keyboardGrabberItems.isEmpty())
1080 ungrabKeyboard(keyboardGrabberItems.constFirst());
1081}
1082
1083void QGraphicsScenePrivate::enableMouseTrackingOnViews()
1084{
1085 for (QGraphicsView *view : std::as_const(views))
1086 view->viewport()->setMouseTracking(true);
1087}
1088
1089/*!
1090 \class QGraphicsScenePrivate
1091 \inmodule QtWidgets
1092 \internal
1093*/
1094
1095/*!
1096 Returns all items for the screen position in \a event.
1097*/
1098QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &screenPos,
1099 const QPointF &scenePos,
1100 QWidget *widget) const
1101{
1102 Q_Q(const QGraphicsScene);
1103 QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
1104 if (!view)
1105 return q->items(scenePos, Qt::IntersectsItemShape, Qt::DescendingOrder, QTransform());
1106
1107 const QRectF pointRect(QPointF(widget->mapFromGlobal(screenPos)), QSizeF(1, 1));
1108 if (!view->isTransformed())
1109 return q->items(pointRect, Qt::IntersectsItemShape, Qt::DescendingOrder);
1110
1111 const QTransform viewTransform = view->viewportTransform();
1112 if (viewTransform.type() <= QTransform::TxScale) {
1113 return q->items(viewTransform.inverted().mapRect(pointRect), Qt::IntersectsItemShape,
1114 Qt::DescendingOrder, viewTransform);
1115 }
1116 return q->items(viewTransform.inverted().map(pointRect), Qt::IntersectsItemShape,
1117 Qt::DescendingOrder, viewTransform);
1118}
1119
1120/*!
1121 \internal
1122*/
1123void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event)
1124{
1125 for (int i = 0x1; i <= 0x10; i <<= 1) {
1126 if (event->buttons() & i) {
1127 mouseGrabberButtonDownPos.insert(Qt::MouseButton(i),
1128 mouseGrabberItems.constLast()->d_ptr->genericMapFromScene(event->scenePos(),
1129 event->widget()));
1130 mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos());
1131 mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos());
1132 }
1133 }
1134}
1135
1136/*!
1137 \internal
1138*/
1139void QGraphicsScenePrivate::installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
1140{
1141 sceneEventFilters.insert(watched, filter);
1142}
1143
1144/*!
1145 \internal
1146*/
1147void QGraphicsScenePrivate::removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
1148{
1149 if (!sceneEventFilters.contains(watched))
1150 return;
1151
1152 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(watched);
1153 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(watched);
1154 do {
1155 if (it.value() == filter)
1156 it = sceneEventFilters.erase(it);
1157 else
1158 ++it;
1159 } while (it != end);
1160}
1161
1162/*!
1163 \internal
1164*/
1165bool QGraphicsScenePrivate::filterDescendantEvent(QGraphicsItem *item, QEvent *event)
1166{
1167 if (item && (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents)) {
1168 QGraphicsItem *parent = item->parentItem();
1169 while (parent) {
1170 if (parent->d_ptr->filtersDescendantEvents && parent->sceneEventFilter(item, event))
1171 return true;
1172 if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents))
1173 return false;
1174 parent = parent->parentItem();
1175 }
1176 }
1177 return false;
1178}
1179
1180/*!
1181 \internal
1182*/
1183bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event)
1184{
1185 if (item && !sceneEventFilters.contains(item))
1186 return false;
1187
1188 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(item);
1189 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(item);
1190 while (it != end) {
1191 // ### The filterer and filteree might both be deleted.
1192 if (it.value()->sceneEventFilter(it.key(), event))
1193 return true;
1194 ++it;
1195 }
1196 return false;
1197}
1198
1199/*!
1200 \internal
1201
1202 This is the final dispatch point for any events from the scene to the
1203 item. It filters the event first - if the filter returns \c true, the event
1204 is considered to have been eaten by the filter, and is therefore stopped
1205 (the default filter returns \c false). Then/otherwise, if the item is
1206 enabled, the event is sent; otherwise it is stopped.
1207*/
1208bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event)
1209{
1210#if QT_CONFIG(gestures)
1211 if (QGraphicsObject *object = item->toGraphicsObject()) {
1212 QGestureManager *gestureManager = QApplicationPrivate::instance()->gestureManager;
1213 if (gestureManager) {
1214 if (gestureManager->filterEvent(object, event))
1215 return true;
1216 }
1217 }
1218#endif // QT_CONFIG(gestures)
1219
1220 if (filterEvent(item, event))
1221 return false;
1222 if (filterDescendantEvent(item, event))
1223 return false;
1224 if (!item || !item->isEnabled())
1225 return false;
1226 if (QGraphicsObject *o = item->toGraphicsObject()) {
1227 bool spont = event->spontaneous();
1228 if (spont ? qt_sendSpontaneousEvent(o, event) : QCoreApplication::sendEvent(o, event))
1229 return true;
1230 event->m_spont = spont;
1231 }
1232 return item->sceneEvent(event);
1233}
1234
1235/*!
1236 \internal
1237*/
1238void QGraphicsScenePrivate::cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
1239 QGraphicsSceneDragDropEvent *source)
1240{
1241 dest->setWidget(source->widget());
1242 dest->setPos(source->pos());
1243 dest->setScenePos(source->scenePos());
1244 dest->setScreenPos(source->screenPos());
1245 dest->setButtons(source->buttons());
1246 dest->setModifiers(source->modifiers());
1247 dest->setPossibleActions(source->possibleActions());
1248 dest->setProposedAction(source->proposedAction());
1249 dest->setDropAction(source->dropAction());
1250 dest->setSource(source->source());
1251 dest->setMimeData(source->mimeData());
1252}
1253
1254/*!
1255 \internal
1256*/
1257void QGraphicsScenePrivate::sendDragDropEvent(QGraphicsItem *item,
1258 QGraphicsSceneDragDropEvent *dragDropEvent)
1259{
1260 dragDropEvent->setPos(item->d_ptr->genericMapFromScene(dragDropEvent->scenePos(), dragDropEvent->widget()));
1261 sendEvent(item, dragDropEvent);
1262}
1263
1264/*!
1265 \internal
1266*/
1267void QGraphicsScenePrivate::sendHoverEvent(QEvent::Type type, QGraphicsItem *item,
1268 QGraphicsSceneHoverEvent *hoverEvent)
1269{
1270 QGraphicsSceneHoverEvent event(type);
1271 event.setWidget(hoverEvent->widget());
1272 const QTransform mapFromScene = item->d_ptr->genericMapFromSceneTransform(hoverEvent->widget());
1273 event.setPos(mapFromScene.map(hoverEvent->scenePos()));
1274 event.setScenePos(hoverEvent->scenePos());
1275 event.setScreenPos(hoverEvent->screenPos());
1276 event.setLastPos(mapFromScene.map(hoverEvent->lastScenePos()));
1277 event.setLastScenePos(hoverEvent->lastScenePos());
1278 event.setLastScreenPos(hoverEvent->lastScreenPos());
1279 event.setModifiers(hoverEvent->modifiers());
1280 sendEvent(item, &event);
1281}
1282
1283/*!
1284 \internal
1285*/
1286void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent)
1287{
1288 if (mouseEvent->button() == 0 && mouseEvent->buttons() == 0 && lastMouseGrabberItemHasImplicitMouseGrab) {
1289 // ### This is a temporary fix for until we get proper mouse
1290 // grab events.
1291 clearMouseGrabber();
1292 return;
1293 }
1294
1295 QGraphicsItem *item = mouseGrabberItems.constLast();
1296 if (item->isBlockedByModalPanel())
1297 return;
1298
1299 const QTransform mapFromScene = item->d_ptr->genericMapFromSceneTransform(mouseEvent->widget());
1300 const QPointF itemPos = mapFromScene.map(mouseEvent->scenePos());
1301 for (int i = 0x1; i <= 0x10; i <<= 1) {
1302 Qt::MouseButton button = Qt::MouseButton(i);
1303 mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, itemPos));
1304 mouseEvent->setButtonDownScenePos(button, mouseGrabberButtonDownScenePos.value(button, mouseEvent->scenePos()));
1305 mouseEvent->setButtonDownScreenPos(button, mouseGrabberButtonDownScreenPos.value(button, mouseEvent->screenPos()));
1306 }
1307 mouseEvent->setPos(itemPos);
1308 mouseEvent->setLastPos(mapFromScene.map(mouseEvent->lastScenePos()));
1309 sendEvent(item, mouseEvent);
1310}
1311
1312/*!
1313 \internal
1314*/
1315void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent)
1316{
1317 Q_Q(QGraphicsScene);
1318
1319 // Ignore by default, unless we find a mouse grabber that accepts it.
1320 mouseEvent->ignore();
1321
1322 // Deliver to any existing mouse grabber.
1323 if (!mouseGrabberItems.isEmpty()) {
1324 if (mouseGrabberItems.constLast()->isBlockedByModalPanel())
1325 return;
1326 // The event is ignored by default, but we disregard the event's
1327 // accepted state after delivery; the mouse is grabbed, after all.
1328 sendMouseEvent(mouseEvent);
1329 return;
1330 }
1331
1332 // Start by determining the number of items at the current position.
1333 // Reuse value from earlier calculations if possible.
1334 if (cachedItemsUnderMouse.isEmpty()) {
1335 cachedItemsUnderMouse = itemsAtPosition(mouseEvent->screenPos(),
1336 mouseEvent->scenePos(),
1337 mouseEvent->widget());
1338 }
1339
1340 // Update window activation.
1341 QGraphicsItem *topItem = cachedItemsUnderMouse.value(0);
1342 QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : nullptr;
1343 if (newActiveWindow && newActiveWindow->isBlockedByModalPanel(&topItem)) {
1344 // pass activation to the blocking modal window
1345 newActiveWindow = topItem ? topItem->window() : nullptr;
1346 }
1347
1348 if (newActiveWindow != q->activeWindow())
1349 q->setActiveWindow(newActiveWindow);
1350
1351 // Set focus on the topmost enabled item that can take focus.
1352 bool setFocus = false;
1353
1354 for (QGraphicsItem *item : std::as_const(cachedItemsUnderMouse)) {
1355 if (item->isBlockedByModalPanel()
1356 || (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling)) {
1357 // Make sure we don't clear focus.
1358 setFocus = true;
1359 break;
1360 }
1361 if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable))) {
1362 if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
1363 setFocus = true;
1364 if (item != q->focusItem() && item->d_ptr->mouseSetsFocus)
1365 q->setFocusItem(item, Qt::MouseFocusReason);
1366 break;
1367 }
1368 }
1369 if (item->isPanel())
1370 break;
1371 if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation)
1372 break;
1373 }
1374
1375 // Check for scene modality.
1376 bool sceneModality = false;
1377 for (auto modalPanel : std::as_const(modalPanels)) {
1378 if (modalPanel->panelModality() == QGraphicsItem::SceneModal) {
1379 sceneModality = true;
1380 break;
1381 }
1382 }
1383
1384 // If nobody could take focus, clear it.
1385 if (!stickyFocus && !setFocus && !sceneModality)
1386 q->setFocusItem(nullptr, Qt::MouseFocusReason);
1387
1388 // Any item will do.
1389 if (sceneModality && cachedItemsUnderMouse.isEmpty())
1390 cachedItemsUnderMouse << modalPanels.constFirst();
1391
1392 // Find a mouse grabber by sending mouse press events to all mouse grabber
1393 // candidates one at a time, until the event is accepted. It's accepted by
1394 // default, so the receiver has to explicitly ignore it for it to pass
1395 // through.
1396 for (QGraphicsItem *item : std::as_const(cachedItemsUnderMouse)) {
1397 if (!(item->acceptedMouseButtons() & mouseEvent->button())) {
1398 // Skip items that don't accept the event's mouse button.
1399 continue;
1400 }
1401
1402 // Check if this item is blocked by a modal panel and deliver the mouse event to the
1403 // blocking panel instead of this item if blocked.
1404 (void) item->isBlockedByModalPanel(&item);
1405
1406 grabMouse(item, /* implicit = */ true);
1407 mouseEvent->accept();
1408
1409 // check if the item we are sending to are disabled (before we send the event)
1410 bool disabled = !item->isEnabled();
1411 bool isPanel = item->isPanel();
1412 if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick
1413 && item != lastMouseGrabberItem && lastMouseGrabberItem) {
1414 // If this item is different from the item that received the last
1415 // mouse event, and mouseEvent is a double-click event, then the
1416 // event is converted to a press. Known limitation:
1417 // Triple-clicking will not generate a double-click, though.
1418 QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
1419 mousePress.m_spont = mouseEvent->spontaneous();
1420 mousePress.accept();
1421 mousePress.setButton(mouseEvent->button());
1422 mousePress.setButtons(mouseEvent->buttons());
1423 mousePress.setScreenPos(mouseEvent->screenPos());
1424 mousePress.setScenePos(mouseEvent->scenePos());
1425 mousePress.setModifiers(mouseEvent->modifiers());
1426 mousePress.setWidget(mouseEvent->widget());
1427 mousePress.setButtonDownPos(mouseEvent->button(),
1428 mouseEvent->buttonDownPos(mouseEvent->button()));
1429 mousePress.setButtonDownScenePos(mouseEvent->button(),
1430 mouseEvent->buttonDownScenePos(mouseEvent->button()));
1431 mousePress.setButtonDownScreenPos(mouseEvent->button(),
1432 mouseEvent->buttonDownScreenPos(mouseEvent->button()));
1433 sendMouseEvent(&mousePress);
1434 mouseEvent->setAccepted(mousePress.isAccepted());
1435 } else {
1436 sendMouseEvent(mouseEvent);
1437 }
1438
1439 bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.constLast() != item;
1440 if (disabled) {
1441 ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
1442 break;
1443 }
1444 if (mouseEvent->isAccepted()) {
1445 if (!mouseGrabberItems.isEmpty())
1446 storeMouseButtonsForMouseGrabber(mouseEvent);
1447 lastMouseGrabberItem = item;
1448 return;
1449 }
1450 ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
1451
1452 // Don't propagate through panels.
1453 if (isPanel)
1454 break;
1455 }
1456
1457 // Is the event still ignored? Then the mouse press goes to the scene.
1458 // Reset the mouse grabber, clear the selection, clear focus, and leave
1459 // the event ignored so that it can propagate through the originating
1460 // view.
1461 if (!mouseEvent->isAccepted()) {
1462 clearMouseGrabber();
1463
1464 QGraphicsView *view = mouseEvent->widget() ? qobject_cast<QGraphicsView *>(mouseEvent->widget()->parentWidget()) : 0;
1465 bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag;
1466 bool extendSelection = (mouseEvent->modifiers() & Qt::ControlModifier) != 0;
1467 dontClearSelection |= extendSelection;
1468 if (!dontClearSelection) {
1469 // Clear the selection if the originating view isn't in scroll
1470 // hand drag mode. The view will clear the selection if no drag
1471 // happened.
1472 q->clearSelection();
1473 }
1474 }
1475}
1476
1477/*!
1478 \internal
1479
1480 Ensures that the list of toplevels is sorted by insertion order, and that
1481 the siblingIndexes are packed (no gaps), and start at 0.
1482
1483 ### This function is almost identical to
1484 QGraphicsItemPrivate::ensureSequentialSiblingIndex().
1485*/
1486void QGraphicsScenePrivate::ensureSequentialTopLevelSiblingIndexes()
1487{
1488 if (!topLevelSequentialOrdering) {
1489 std::sort(topLevelItems.begin(), topLevelItems.end(), QGraphicsItemPrivate::insertionOrder);
1490 topLevelSequentialOrdering = true;
1491 needSortTopLevelItems = 1;
1492 }
1493 if (holesInTopLevelSiblingIndex) {
1494 holesInTopLevelSiblingIndex = 0;
1495 for (int i = 0; i < topLevelItems.size(); ++i)
1496 topLevelItems[i]->d_ptr->siblingIndex = i;
1497 }
1498}
1499
1500/*!
1501 \internal
1502
1503 Set the font and propagate the changes if the font is different from the
1504 current font.
1505*/
1506void QGraphicsScenePrivate::setFont_helper(const QFont &font)
1507{
1508 if (this->font == font && this->font.resolveMask() == font.resolveMask())
1509 return;
1510 updateFont(font);
1511}
1512
1513/*!
1514 \internal
1515
1516 Resolve the scene's font against the application font, and propagate the
1517 changes too all items in the scene.
1518*/
1519void QGraphicsScenePrivate::resolveFont()
1520{
1521 QFont naturalFont = QApplication::font();
1522 naturalFont.setResolveMask(0);
1523 QFont resolvedFont = font.resolve(naturalFont);
1524 updateFont(resolvedFont);
1525}
1526
1527/*!
1528 \internal
1529
1530 Update the font, and whether or not it has changed, reresolve all fonts in
1531 the scene.
1532*/
1533void QGraphicsScenePrivate::updateFont(const QFont &font)
1534{
1535 Q_Q(QGraphicsScene);
1536
1537 // Update local font setting.
1538 this->font = font;
1539
1540 // Resolve the fonts of all top-level widget items, or widget items
1541 // whose parent is not a widget.
1542 const auto items = q->items();
1543 for (QGraphicsItem *item : items) {
1544 if (!item->parentItem()) {
1545 // Resolvefont for an item is a noop operation, but
1546 // every item can be a widget, or can have a widget
1547 // childre.
1548 item->d_ptr->resolveFont(font.resolveMask());
1549 }
1550 }
1551
1552 // Send the scene a FontChange event.
1553 QEvent event(QEvent::FontChange);
1554 QCoreApplication::sendEvent(q, &event);
1555}
1556
1557/*!
1558 \internal
1559
1560 Set the palette and propagate the changes if the palette is different from
1561 the current palette.
1562*/
1563void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette)
1564{
1565 if (this->palette == palette && this->palette.resolveMask() == palette.resolveMask())
1566 return;
1567 updatePalette(palette);
1568}
1569
1570/*!
1571 \internal
1572
1573 Resolve the scene's palette against the application palette, and propagate
1574 the changes too all items in the scene.
1575*/
1576void QGraphicsScenePrivate::resolvePalette()
1577{
1578 QPalette naturalPalette = QGuiApplication::palette();
1579 naturalPalette.setResolveMask(0);
1580 QPalette resolvedPalette = palette.resolve(naturalPalette);
1581 updatePalette(resolvedPalette);
1582}
1583
1584/*!
1585 \internal
1586
1587 Update the palette, and whether or not it has changed, reresolve all
1588 palettes in the scene.
1589*/
1590void QGraphicsScenePrivate::updatePalette(const QPalette &palette)
1591{
1592 Q_Q(QGraphicsScene);
1593
1594 // Update local palette setting.
1595 this->palette = palette;
1596
1597 // Resolve the palettes of all top-level widget items, or widget items
1598 // whose parent is not a widget.
1599 const auto items = q->items();
1600 for (QGraphicsItem *item : items) {
1601 if (!item->parentItem()) {
1602 // ResolvePalette for an item is a noop operation, but
1603 // every item can be a widget, or can have a widget
1604 // children.
1605 item->d_ptr->resolvePalette(palette.resolveMask());
1606 }
1607 }
1608
1609 // Send the scene a PaletteChange event.
1610 QEvent event(QEvent::PaletteChange);
1611 QCoreApplication::sendEvent(q, &event);
1612}
1613
1614/*!
1615 Constructs a QGraphicsScene object. The \a parent parameter is
1616 passed to QObject's constructor.
1617*/
1618QGraphicsScene::QGraphicsScene(QObject *parent)
1619 : QObject(*new QGraphicsScenePrivate, parent)
1620{
1621 d_func()->init();
1622}
1623
1624/*!
1625 Constructs a QGraphicsScene object, using \a sceneRect for its
1626 scene rectangle. The \a parent parameter is passed to QObject's
1627 constructor.
1628
1629 \sa sceneRect
1630*/
1631QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent)
1632 : QObject(*new QGraphicsScenePrivate, parent)
1633{
1634 d_func()->init();
1635 setSceneRect(sceneRect);
1636}
1637
1638/*!
1639 Constructs a QGraphicsScene object, using the rectangle specified
1640 by (\a x, \a y), and the given \a width and \a height for its
1641 scene rectangle. The \a parent parameter is passed to QObject's
1642 constructor.
1643
1644 \sa sceneRect
1645*/
1646QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent)
1647 : QObject(*new QGraphicsScenePrivate, parent)
1648{
1649 d_func()->init();
1650 setSceneRect(x, y, width, height);
1651}
1652
1653/*!
1654 Removes and deletes all items from the scene object
1655 before destroying the scene object. The scene object
1656 is removed from the application's global scene list,
1657 and it is removed from all associated views.
1658*/
1659QGraphicsScene::~QGraphicsScene()
1660{
1661 Q_D(QGraphicsScene);
1662
1663 // Remove this scene from qApp's global scene list.
1664 if (!QApplicationPrivate::is_app_closing)
1665 qApp->d_func()->scene_list.removeAll(this);
1666
1667 clear();
1668
1669 // Remove this scene from all associated views.
1670 // Note: d->views is modified by QGraphicsView::setScene, so must make a copy
1671 const auto views = d->views;
1672 for (auto view : std::as_const(views))
1673 view->setScene(nullptr);
1674}
1675
1676/*!
1677 \property QGraphicsScene::sceneRect
1678 \brief the scene rectangle; the bounding rectangle of the scene
1679
1680 The scene rectangle defines the extent of the scene. It is
1681 primarily used by QGraphicsView to determine the view's default
1682 scrollable area, and by QGraphicsScene to manage item indexing.
1683
1684 If unset, or if set to a null QRectF, sceneRect() will return the largest
1685 bounding rect of all items on the scene since the scene was created (i.e.,
1686 a rectangle that grows when items are added to or moved in the scene, but
1687 never shrinks).
1688
1689 \sa width(), height(), QGraphicsView::sceneRect
1690*/
1691QRectF QGraphicsScene::sceneRect() const
1692{
1693 Q_D(const QGraphicsScene);
1694 if (d->hasSceneRect)
1695 return d->sceneRect;
1696
1697 if (d->dirtyGrowingItemsBoundingRect) {
1698 // Lazily update the growing items bounding rect
1699 QGraphicsScenePrivate *thatd = const_cast<QGraphicsScenePrivate *>(d);
1700 QRectF oldGrowingBoundingRect = thatd->growingItemsBoundingRect;
1701 thatd->growingItemsBoundingRect |= itemsBoundingRect();
1702 thatd->dirtyGrowingItemsBoundingRect = false;
1703 if (oldGrowingBoundingRect != thatd->growingItemsBoundingRect)
1704 emit const_cast<QGraphicsScene *>(this)->sceneRectChanged(thatd->growingItemsBoundingRect);
1705 }
1706 return d->growingItemsBoundingRect;
1707}
1708void QGraphicsScene::setSceneRect(const QRectF &rect)
1709{
1710 Q_D(QGraphicsScene);
1711 if (rect != d->sceneRect) {
1712 d->hasSceneRect = !rect.isNull();
1713 d->sceneRect = rect;
1714 emit sceneRectChanged(d->hasSceneRect ? rect : d->growingItemsBoundingRect);
1715 }
1716}
1717
1718/*!
1719 \fn qreal QGraphicsScene::width() const
1720
1721 This convenience function is equivalent to calling sceneRect().width().
1722
1723 \sa height()
1724*/
1725
1726/*!
1727 \fn qreal QGraphicsScene::height() const
1728
1729 This convenience function is equivalent to calling \c sceneRect().height().
1730
1731 \sa width()
1732*/
1733
1734/*!
1735 Renders the \a source rect from scene into \a target, using \a painter. This
1736 function is useful for capturing the contents of the scene onto a paint
1737 device, such as a QImage (e.g., to take a screenshot), or for printing
1738 with QPrinter. For example:
1739
1740 \snippet code/src_gui_graphicsview_qgraphicsscene.cpp 1
1741
1742 If \a source is a null rect, this function will use sceneRect() to
1743 determine what to render. If \a target is a null rect, the dimensions of \a
1744 painter's paint device will be used.
1745
1746 The source rect contents will be transformed according to \a
1747 aspectRatioMode to fit into the target rect. By default, the aspect ratio
1748 is kept, and \a source is scaled to fit in \a target.
1749
1750 \sa QGraphicsView::render()
1751*/
1752void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source,
1753 Qt::AspectRatioMode aspectRatioMode)
1754{
1755 // ### Switch to using the recursive rendering algorithm instead.
1756
1757 // Default source rect = scene rect
1758 QRectF sourceRect = source;
1759 if (sourceRect.isNull())
1760 sourceRect = sceneRect();
1761
1762 // Default target rect = device rect
1763 QRectF targetRect = target;
1764 if (targetRect.isNull()) {
1765 if (painter->device()->devType() == QInternal::Picture)
1766 targetRect = sourceRect;
1767 else
1768 targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
1769 }
1770
1771 // Find the ideal x / y scaling ratio to fit \a source into \a target.
1772 qreal xratio = targetRect.width() / sourceRect.width();
1773 qreal yratio = targetRect.height() / sourceRect.height();
1774
1775 // Scale according to the aspect ratio mode.
1776 switch (aspectRatioMode) {
1777 case Qt::KeepAspectRatio:
1778 xratio = yratio = qMin(xratio, yratio);
1779 break;
1780 case Qt::KeepAspectRatioByExpanding:
1781 xratio = yratio = qMax(xratio, yratio);
1782 break;
1783 case Qt::IgnoreAspectRatio:
1784 break;
1785 }
1786
1787 // Find all items to draw, and reverse the list (we want to draw
1788 // in reverse order).
1789 QList<QGraphicsItem *> itemList = items(sourceRect, Qt::IntersectsItemBoundingRect);
1790 QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
1791 const qsizetype numItems = itemList.size();
1792 for (qsizetype i = 0; i < numItems; ++i)
1793 itemArray[numItems - i - 1] = itemList.at(i);
1794 itemList.clear();
1795
1796 painter->save();
1797
1798 // Transform the painter.
1799 painter->setClipRect(targetRect, Qt::IntersectClip);
1800 QTransform painterTransform;
1801 painterTransform *= QTransform()
1802 .translate(targetRect.left(), targetRect.top())
1803 .scale(xratio, yratio)
1804 .translate(-sourceRect.left(), -sourceRect.top());
1805 painter->setWorldTransform(painterTransform, true);
1806
1807 // Generate the style options
1808 QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems];
1809 for (qsizetype i = 0; i < numItems; ++i)
1810 itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterTransform, targetRect.toRect());
1811
1812 // Render the scene.
1813 drawBackground(painter, sourceRect);
1814 drawItems(painter, numItems, itemArray, styleOptionArray);
1815 drawForeground(painter, sourceRect);
1816
1817 delete [] itemArray;
1818 delete [] styleOptionArray;
1819
1820 painter->restore();
1821}
1822
1823/*!
1824 \property QGraphicsScene::itemIndexMethod
1825 \brief the item indexing method.
1826
1827 QGraphicsScene applies an indexing algorithm to the scene, to speed up
1828 item discovery functions like items() and itemAt(). Indexing is most
1829 efficient for static scenes (i.e., where items don't move around). For
1830 dynamic scenes, or scenes with many animated items, the index bookkeeping
1831 can outweigh the fast lookup speeds.
1832
1833 For the common case, the default index method BspTreeIndex works fine. If
1834 your scene uses many animations and you are experiencing slowness, you can
1835 disable indexing by calling \c setItemIndexMethod(NoIndex).
1836
1837 \sa bspTreeDepth
1838*/
1839QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const
1840{
1841 Q_D(const QGraphicsScene);
1842 return d->indexMethod;
1843}
1844void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method)
1845{
1846 Q_D(QGraphicsScene);
1847 if (d->indexMethod == method)
1848 return;
1849
1850 d->indexMethod = method;
1851
1852 QList<QGraphicsItem *> oldItems = d->index->items(Qt::DescendingOrder);
1853 delete d->index;
1854 if (method == BspTreeIndex)
1855 d->index = new QGraphicsSceneBspTreeIndex(this);
1856 else
1857 d->index = new QGraphicsSceneLinearIndex(this);
1858 for (int i = oldItems.size() - 1; i >= 0; --i)
1859 d->index->addItem(oldItems.at(i));
1860}
1861
1862/*!
1863 \property QGraphicsScene::bspTreeDepth
1864 \brief the depth of QGraphicsScene's BSP index tree
1865 \since 4.3
1866
1867 This property has no effect when NoIndex is used.
1868
1869 This value determines the depth of QGraphicsScene's BSP tree. The depth
1870 directly affects QGraphicsScene's performance and memory usage; the latter
1871 growing exponentially with the depth of the tree. With an optimal tree
1872 depth, QGraphicsScene can instantly determine the locality of items, even
1873 for scenes with thousands or millions of items. This also greatly improves
1874 rendering performance.
1875
1876 By default, the value is 0, in which case Qt will guess a reasonable
1877 default depth based on the size, location and number of items in the
1878 scene. If these parameters change frequently, however, you may experience
1879 slowdowns as QGraphicsScene retunes the depth internally. You can avoid
1880 potential slowdowns by fixating the tree depth through setting this
1881 property.
1882
1883 The depth of the tree and the size of the scene rectangle decide the
1884 granularity of the scene's partitioning. The size of each scene segment is
1885 determined by the following algorithm:
1886
1887 \snippet code/src_gui_graphicsview_qgraphicsscene.cpp 2
1888
1889 The BSP tree has an optimal size when each segment contains between 0 and
1890 10 items.
1891
1892 \sa itemIndexMethod
1893*/
1894int QGraphicsScene::bspTreeDepth() const
1895{
1896 Q_D(const QGraphicsScene);
1897 QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index);
1898 return bspTree ? bspTree->bspTreeDepth() : 0;
1899}
1900void QGraphicsScene::setBspTreeDepth(int depth)
1901{
1902 Q_D(QGraphicsScene);
1903 if (depth < 0) {
1904 qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth);
1905 return;
1906 }
1907
1908 QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index);
1909 if (!bspTree) {
1910 qWarning("QGraphicsScene::setBspTreeDepth: cannot apply if indexing method is not BSP");
1911 return;
1912 }
1913 bspTree->setBspTreeDepth(depth);
1914}
1915
1916/*!
1917 Calculates and returns the bounding rect of all items on the scene. This
1918 function works by iterating over all items, and because of this, it can
1919 be slow for large scenes.
1920
1921 \sa sceneRect()
1922*/
1923QRectF QGraphicsScene::itemsBoundingRect() const
1924{
1925 // Does not take untransformable items into account.
1926 QRectF boundingRect;
1927 const auto items_ = items();
1928 for (QGraphicsItem *item : items_)
1929 boundingRect |= item->sceneBoundingRect();
1930 return boundingRect;
1931}
1932
1933/*!
1934 Returns an ordered list of all items on the scene. \a order decides the
1935 stacking order.
1936
1937 \sa addItem(), removeItem(), {QGraphicsItem#Sorting}{Sorting}
1938*/
1939QList<QGraphicsItem *> QGraphicsScene::items(Qt::SortOrder order) const
1940{
1941 Q_D(const QGraphicsScene);
1942 return d->index->items(order);
1943}
1944
1945/*!
1946 \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
1947 \overload
1948 \since 4.6
1949
1950 \brief Returns all visible items that, depending on \a mode, are
1951 either inside or intersect with the rectangle defined by \a x, \a y,
1952 \a w and \a h, in a list sorted using \a order. In this case, "visible" defines items for which:
1953 isVisible() returns \c true, effectiveOpacity() returns a value greater than 0.0
1954 (which is fully transparent) and the parent item does not clip it.
1955
1956 \a deviceTransform is the transformation that applies to the view, and needs to
1957 be provided if the scene contains items that ignore transformations.
1958*/
1959
1960/*!
1961 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
1962 \since 4.6
1963
1964 \brief Returns all visible items that, depending on \a mode, are at
1965 the specified \a pos in a list sorted using \a order. In this case, "visible" defines items for which:
1966 isVisible() returns \c true, effectiveOpacity() returns a value greater than 0.0
1967 (which is fully transparent) and the parent item does not clip it.
1968
1969 The default value for \a mode is Qt::IntersectsItemShape; all items whose
1970 exact shape intersects with \a pos are returned.
1971
1972 \a deviceTransform is the transformation that applies to the view, and needs to
1973 be provided if the scene contains items that ignore transformations.
1974
1975 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
1976*/
1977QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode,
1978 Qt::SortOrder order, const QTransform &deviceTransform) const
1979{
1980 Q_D(const QGraphicsScene);
1981 return d->index->items(pos, mode, order, deviceTransform);
1982}
1983
1984/*!
1985 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
1986 \overload
1987 \since 4.6
1988
1989 \brief Returns all visible items that, depending on \a mode, are
1990 either inside or intersect with the specified \a rect, in a
1991 list sorted using \a order. In this case, "visible" defines items for which:
1992 isVisible() returns \c true, effectiveOpacity() returns a value greater than 0.0
1993 (which is fully transparent) and the parent item does not clip it.
1994
1995 The default value for \a mode is Qt::IntersectsItemShape; all items whose
1996 exact shape intersects with or is contained by \a rect are returned.
1997
1998 \a deviceTransform is the transformation that applies to the view, and needs to
1999 be provided if the scene contains items that ignore transformations.
2000
2001 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2002*/
2003QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode,
2004 Qt::SortOrder order, const QTransform &deviceTransform) const
2005{
2006 Q_D(const QGraphicsScene);
2007 return d->index->items(rect, mode, order, deviceTransform);
2008}
2009
2010/*!
2011 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
2012 \overload
2013 \since 4.6
2014
2015 \brief Returns all visible items that, depending on \a mode, are
2016 either inside or intersect with the specified \a polygon, in
2017 a list sorted using \a order. In this case, "visible" defines items for which:
2018 isVisible() returns \c true, effectiveOpacity() returns a value greater than 0.0
2019 (which is fully transparent) and the parent item does not clip it.
2020
2021 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2022 exact shape intersects with or is contained by \a polygon are returned.
2023
2024 \a deviceTransform is the transformation that applies to the view, and needs to
2025 be provided if the scene contains items that ignore transformations.
2026
2027 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2028*/
2029QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode,
2030 Qt::SortOrder order, const QTransform &deviceTransform) const
2031{
2032 Q_D(const QGraphicsScene);
2033 return d->index->items(polygon, mode, order, deviceTransform);
2034}
2035
2036/*!
2037 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
2038 \overload
2039 \since 4.6
2040
2041 \brief Returns all visible items that, depending on \a mode, are
2042 either inside or intersect with the specified \a path, in a
2043 list sorted using \a order. In this case, "visible" defines items for which:
2044 isVisible() returns \c true, effectiveOpacity() returns a value greater than 0.0
2045 (which is fully transparent) and the parent item does not clip it.
2046
2047 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2048 exact shape intersects with or is contained by \a path are returned.
2049
2050 \a deviceTransform is the transformation that applies to the view, and needs to
2051 be provided if the scene contains items that ignore transformations.
2052
2053 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2054*/
2055QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode,
2056 Qt::SortOrder order, const QTransform &deviceTransform) const
2057{
2058 Q_D(const QGraphicsScene);
2059 return d->index->items(path, mode, order, deviceTransform);
2060}
2061
2062/*!
2063 Returns a list of all items that collide with \a item. Collisions are
2064 determined by calling QGraphicsItem::collidesWithItem(); the collision
2065 detection is determined by \a mode. By default, all items whose shape
2066 intersects \a item or is contained inside \a item's shape are returned.
2067
2068 The items are returned in descending stacking order (i.e., the first item
2069 in the list is the uppermost item, and the last item is the lowermost
2070 item).
2071
2072 \sa items(), itemAt(), QGraphicsItem::collidesWithItem(), {QGraphicsItem#Sorting}{Sorting}
2073*/
2074QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item,
2075 Qt::ItemSelectionMode mode) const
2076{
2077 Q_D(const QGraphicsScene);
2078 if (!item) {
2079 qWarning("QGraphicsScene::collidingItems: cannot find collisions for null item");
2080 return QList<QGraphicsItem *>();
2081 }
2082
2083 // Does not support ItemIgnoresTransformations.
2084 QList<QGraphicsItem *> tmp;
2085 const auto itemsInVicinity = d->index->estimateItems(item->sceneBoundingRect(), Qt::DescendingOrder);
2086 for (QGraphicsItem *itemInVicinity : itemsInVicinity) {
2087 if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode))
2088 tmp << itemInVicinity;
2089 }
2090 return tmp;
2091}
2092
2093/*!
2094 \since 4.6
2095
2096 Returns the topmost visible item at the specified \a position, or \nullptr
2097 if there are no items at this position.
2098
2099 \a deviceTransform is the transformation that applies to the view, and needs to
2100 be provided if the scene contains items that ignore transformations.
2101
2102 Note: See items() for a definition of which items are considered visible by this function.
2103
2104 \sa items(), collidingItems(), {QGraphicsItem#Sorting}{Sorting}
2105*/
2106QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position, const QTransform &deviceTransform) const
2107{
2108 const QList<QGraphicsItem *> itemsAtPoint = items(position, Qt::IntersectsItemShape,
2109 Qt::DescendingOrder, deviceTransform);
2110 return itemsAtPoint.isEmpty() ? nullptr : itemsAtPoint.first();
2111}
2112
2113/*!
2114 \fn QGraphicsScene::itemAt(qreal x, qreal y, const QTransform &deviceTransform) const
2115 \overload
2116 \since 4.6
2117
2118 Returns the topmost visible item at the position specified by (\a x, \a
2119 y), or \nullptr if there are no items at this position.
2120
2121 \a deviceTransform is the transformation that applies to the view, and needs to
2122 be provided if the scene contains items that ignore transformations.
2123
2124 This convenience function is equivalent to calling \c
2125 {itemAt(QPointF(x, y), deviceTransform)}.
2126
2127 Note: See items() for a definition of which items are considered visible by this function.
2128*/
2129
2130/*!
2131 Returns a list of all currently selected items. The items are
2132 returned in no particular order.
2133
2134 \sa setSelectionArea()
2135*/
2136QList<QGraphicsItem *> QGraphicsScene::selectedItems() const
2137{
2138 Q_D(const QGraphicsScene);
2139
2140 // Optimization: Lazily removes items that are not selected.
2141 QGraphicsScene *that = const_cast<QGraphicsScene *>(this);
2142 QSet<QGraphicsItem *> actuallySelectedSet;
2143 for (QGraphicsItem *item : std::as_const(that->d_func()->selectedItems)) {
2144 if (item->isSelected())
2145 actuallySelectedSet << item;
2146 }
2147
2148 that->d_func()->selectedItems = actuallySelectedSet;
2149
2150 return d->selectedItems.values();
2151}
2152
2153/*!
2154 Returns the selection area that was previously set with
2155 setSelectionArea(), or an empty QPainterPath if no selection area has been
2156 set.
2157
2158 \sa setSelectionArea()
2159*/
2160QPainterPath QGraphicsScene::selectionArea() const
2161{
2162 Q_D(const QGraphicsScene);
2163 return d->selectionArea;
2164}
2165
2166/*!
2167 \since 4.6
2168
2169 Sets the selection area to \a path. All items within this area are
2170 immediately selected, and all items outside are unselected. You can get
2171 the list of all selected items by calling selectedItems().
2172
2173 \a deviceTransform is the transformation that applies to the view, and needs to
2174 be provided if the scene contains items that ignore transformations.
2175
2176 For an item to be selected, it must be marked as \e selectable
2177 (QGraphicsItem::ItemIsSelectable).
2178
2179 \sa clearSelection(), selectionArea()
2180*/
2181void QGraphicsScene::setSelectionArea(const QPainterPath &path, const QTransform &deviceTransform)
2182{
2183 setSelectionArea(path, Qt::ReplaceSelection, Qt::IntersectsItemShape, deviceTransform);
2184}
2185
2186/*!
2187 \overload
2188 \since 5.5
2189
2190 Sets the selection area to \a path using \a mode to determine if items are
2191 included in the selection area.
2192
2193 \a deviceTransform is the transformation that applies to the view, and needs to
2194 be provided if the scene contains items that ignore transformations.
2195
2196 \a selectionOperation determines what to do with the currently selected items.
2197
2198 \sa clearSelection(), selectionArea()
2199*/
2200void QGraphicsScene::setSelectionArea(const QPainterPath &path,
2201 Qt::ItemSelectionOperation selectionOperation,
2202 Qt::ItemSelectionMode mode,
2203 const QTransform &deviceTransform)
2204{
2205 Q_D(QGraphicsScene);
2206
2207 // Note: with boolean path operations, we can improve performance here
2208 // quite a lot by "growing" the old path instead of replacing it. That
2209 // allows us to only check the intersect area for changes, instead of
2210 // reevaluating the whole path over again.
2211 d->selectionArea = path;
2212
2213 QSet<QGraphicsItem *> unselectItems = d->selectedItems;
2214
2215 // Disable emitting selectionChanged() for individual items.
2216 ++d->selectionChanging;
2217 bool changed = false;
2218
2219 // Set all items in path to selected.
2220 const auto items = this->items(path, mode, Qt::DescendingOrder, deviceTransform);
2221 for (QGraphicsItem *item : items) {
2222 if (item->flags() & QGraphicsItem::ItemIsSelectable) {
2223 if (!item->isSelected())
2224 changed = true;
2225 unselectItems.remove(item);
2226 item->setSelected(true);
2227 }
2228 }
2229
2230 switch (selectionOperation) {
2231 case Qt::ReplaceSelection:
2232 // Deselect all items outside path.
2233 for (QGraphicsItem *item : std::as_const(unselectItems)) {
2234 item->setSelected(false);
2235 changed = true;
2236 }
2237 break;
2238 default:
2239 break;
2240 }
2241
2242 // Re-enable emitting selectionChanged() for individual items.
2243 --d->selectionChanging;
2244
2245 if (!d->selectionChanging && changed)
2246 emit selectionChanged();
2247}
2248
2249/*!
2250 Clears the current selection.
2251
2252 \sa setSelectionArea(), selectedItems()
2253*/
2254void QGraphicsScene::clearSelection()
2255{
2256 Q_D(QGraphicsScene);
2257
2258 // Disable emitting selectionChanged
2259 ++d->selectionChanging;
2260 // iterate over a copy, as clearing selection might invalidate selectedItems
2261 const auto selectedItems = d->selectedItems;
2262 QSet<QGraphicsItem *> stillSelectedSet;
2263
2264 for (QGraphicsItem *item : selectedItems) {
2265 item->setSelected(false);
2266 // items might override itemChange to prevent deselection
2267 if (item->isSelected())
2268 stillSelectedSet << item;
2269 }
2270 const bool changed = stillSelectedSet != selectedItems;
2271 d->selectedItems = stillSelectedSet;
2272
2273 // Re-enable emitting selectionChanged() for individual items.
2274 --d->selectionChanging;
2275
2276 if (!d->selectionChanging && changed)
2277 emit selectionChanged();
2278}
2279
2280/*!
2281 \since 4.4
2282
2283 Removes and deletes all items from the scene, but otherwise leaves the
2284 state of the scene unchanged.
2285
2286 \sa addItem()
2287*/
2288void QGraphicsScene::clear()
2289{
2290 Q_D(QGraphicsScene);
2291 // NB! We have to clear the index before deleting items; otherwise the
2292 // index might try to access dangling item pointers.
2293 d->index->clear();
2294 // NB! QGraphicsScenePrivate::unregisterTopLevelItem() removes items
2295 while (!d->topLevelItems.isEmpty())
2296 delete d->topLevelItems.first();
2297 Q_ASSERT(d->topLevelItems.isEmpty());
2298 d->lastItemCount = 0;
2299 d->allItemsIgnoreHoverEvents = true;
2300 d->allItemsUseDefaultCursor = true;
2301 d->allItemsIgnoreTouchEvents = true;
2302 d->focusOnTouch = true;
2303}
2304
2305/*!
2306 Groups all items in \a items into a new QGraphicsItemGroup, and returns a
2307 pointer to the group. The group is created with the common ancestor of \a
2308 items as its parent, and with position (0, 0). The items are all
2309 reparented to the group, and their positions and transformations are
2310 mapped to the group. If \a items is empty, this function will return an
2311 empty top-level QGraphicsItemGroup.
2312
2313 QGraphicsScene has ownership of the group item; you do not need to delete
2314 it. To dismantle (ungroup) a group, call destroyItemGroup().
2315
2316 \sa destroyItemGroup(), QGraphicsItemGroup::addToGroup()
2317*/
2318QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList<QGraphicsItem *> &items)
2319{
2320 // Build a list of the first item's ancestors
2321 QList<QGraphicsItem *> ancestors;
2322 int n = 0;
2323 if (!items.isEmpty()) {
2324 QGraphicsItem *parent = items.at(n++);
2325 while ((parent = parent->parentItem()))
2326 ancestors.append(parent);
2327 }
2328
2329 // Find the common ancestor for all items
2330 QGraphicsItem *commonAncestor = nullptr;
2331 if (!ancestors.isEmpty()) {
2332 while (n < items.size()) {
2333 int commonIndex = -1;
2334 QGraphicsItem *parent = items.at(n++);
2335 do {
2336 int index = ancestors.indexOf(parent, qMax(0, commonIndex));
2337 if (index != -1) {
2338 commonIndex = index;
2339 break;
2340 }
2341 } while ((parent = parent->parentItem()));
2342
2343 if (commonIndex == -1) {
2344 commonAncestor = nullptr;
2345 break;
2346 }
2347
2348 commonAncestor = ancestors.at(commonIndex);
2349 }
2350 }
2351
2352 // Create a new group at that level
2353 QGraphicsItemGroup *group = new QGraphicsItemGroup(commonAncestor);
2354 if (!commonAncestor)
2355 addItem(group);
2356 for (QGraphicsItem *item : items)
2357 group->addToGroup(item);
2358 return group;
2359}
2360
2361/*!
2362 Reparents all items in \a group to \a group's parent item, then removes \a
2363 group from the scene, and finally deletes it. The items' positions and
2364 transformations are mapped from the group to the group's parent.
2365
2366 \sa createItemGroup(), QGraphicsItemGroup::removeFromGroup()
2367*/
2368void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group)
2369{
2370 const auto items = group->childItems();
2371 for (QGraphicsItem *item : items)
2372 group->removeFromGroup(item);
2373 removeItem(group);
2374 delete group;
2375}
2376
2377/*!
2378 Adds or moves the \a item and all its children to this scene.
2379 This scene takes ownership of the \a item.
2380
2381 If the item is visible (i.e., QGraphicsItem::isVisible() returns
2382 true), QGraphicsScene will emit changed() once control goes back
2383 to the event loop.
2384
2385 If the item is already in a different scene, it will first be
2386 removed from its old scene, and then added to this scene as a
2387 top-level.
2388
2389 QGraphicsScene will send ItemSceneChange notifications to \a item
2390 while it is added to the scene. If item does not currently belong
2391 to a scene, only one notification is sent. If it does belong to
2392 scene already (i.e., it is moved to this scene), QGraphicsScene
2393 will send an addition notification as the item is removed from its
2394 previous scene.
2395
2396 If the item is a panel, the scene is active, and there is no
2397 active panel in the scene, then the item will be activated.
2398
2399 \sa removeItem(), addEllipse(), addLine(), addPath(), addPixmap(),
2400 addRect(), addText(), addWidget(), {QGraphicsItem#Sorting}{Sorting}
2401*/
2402void QGraphicsScene::addItem(QGraphicsItem *item)
2403{
2404 Q_D(QGraphicsScene);
2405 if (!item) {
2406 qWarning("QGraphicsScene::addItem: cannot add null item");
2407 return;
2408 }
2409 if (item->d_ptr->scene == this) {
2410 qWarning("QGraphicsScene::addItem: item has already been added to this scene");
2411 return;
2412 }
2413 // Remove this item from its existing scene
2414 if (QGraphicsScene *oldScene = item->d_ptr->scene)
2415 oldScene->removeItem(item);
2416
2417 // Notify the item that its scene is changing, and allow the item to
2418 // react.
2419 const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
2420 QVariant::fromValue<QGraphicsScene *>(this)));
2421 QGraphicsScene *targetScene = qvariant_cast<QGraphicsScene *>(newSceneVariant);
2422 if (targetScene != this) {
2423 if (targetScene && item->d_ptr->scene != targetScene)
2424 targetScene->addItem(item);
2425 return;
2426 }
2427
2428 if (d->unpolishedItems.isEmpty()) {
2429 QMetaMethod method = metaObject()->method(d->polishItemsIndex);
2430 method.invoke(this, Qt::QueuedConnection);
2431 }
2432 d->unpolishedItems.append(item);
2433 item->d_ptr->pendingPolish = true;
2434
2435 // Detach this item from its parent if the parent's scene is different
2436 // from this scene.
2437 if (QGraphicsItem *itemParent = item->d_ptr->parent) {
2438 if (itemParent->d_ptr->scene != this)
2439 item->setParentItem(nullptr);
2440 }
2441
2442 // Add the item to this scene
2443 item->d_func()->scene = targetScene;
2444
2445 // Add the item in the index
2446 d->index->addItem(item);
2447
2448 // Add to list of toplevels if this item is a toplevel.
2449 if (!item->d_ptr->parent)
2450 d->registerTopLevelItem(item);
2451
2452 // Add to list of items that require an update. We cannot assume that the
2453 // item is fully constructed, so calling item->update() can lead to a pure
2454 // virtual function call to boundingRect().
2455 d->markDirty(item);
2456 d->dirtyGrowingItemsBoundingRect = true;
2457
2458 // Disable selectionChanged() for individual items
2459 ++d->selectionChanging;
2460 int oldSelectedItemSize = d->selectedItems.size();
2461
2462 // Enable mouse tracking if we haven't already done so, and the item needs it.
2463 // We cannot use itemAcceptsHoverEvents_helper() here, since we need to enable
2464 // mouse tracking also if this item is temporarily blocked by a modal panel.
2465
2466 auto needsMouseTracking = [](const QGraphicsItemPrivate *item) {
2467 return item->acceptsHover
2468 || (item->isWidget && static_cast<const QGraphicsWidgetPrivate *>(item)->hasDecoration());
2469 };
2470
2471 if (d->allItemsIgnoreHoverEvents && needsMouseTracking(item->d_ptr.data())) {
2472 d->allItemsIgnoreHoverEvents = false;
2473 d->enableMouseTrackingOnViews();
2474 }
2475#ifndef QT_NO_CURSOR
2476 if (d->allItemsUseDefaultCursor && item->d_ptr->hasCursor) {
2477 d->allItemsUseDefaultCursor = false;
2478 if (d->allItemsIgnoreHoverEvents) // already enabled otherwise
2479 d->enableMouseTrackingOnViews();
2480 }
2481#endif //QT_NO_CURSOR
2482
2483 // Enable touch events if the item accepts touch events.
2484 if (d->allItemsIgnoreTouchEvents && item->d_ptr->acceptTouchEvents) {
2485 d->allItemsIgnoreTouchEvents = false;
2486 d->enableTouchEventsOnViews();
2487 }
2488
2489#ifndef QT_NO_GESTURES
2490 for (auto it = item->d_ptr->gestureContext.constBegin();
2491 it != item->d_ptr->gestureContext.constEnd(); ++it)
2492 d->grabGesture(item, it.key());
2493#endif
2494
2495 // Update selection lists
2496 if (item->isSelected())
2497 d->selectedItems << item;
2498 if (item->isWidget() && item->isVisible() && static_cast<QGraphicsWidget *>(item)->windowType() == Qt::Popup)
2499 d->addPopup(static_cast<QGraphicsWidget *>(item));
2500 if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
2501 d->enterModal(item);
2502
2503 // Update creation order focus chain. Make sure to leave the widget's
2504 // internal tab order intact.
2505 if (item->isWidget()) {
2506 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
2507 if (!d->tabFocusFirst) {
2508 // No first tab focus widget - make this the first tab focus
2509 // widget.
2510 d->tabFocusFirst = widget;
2511 } else if (!widget->parentWidget() && !widget->isPanel()) {
2512 // Adding a widget that is not part of a tab focus chain.
2513 QGraphicsWidget *myNewPrev = d->tabFocusFirst->d_func()->focusPrev;
2514 myNewPrev->d_func()->focusNext = widget;
2515 widget->d_func()->focusPrev->d_func()->focusNext = d->tabFocusFirst;
2516 d->tabFocusFirst->d_func()->focusPrev = widget->d_func()->focusPrev;
2517 widget->d_func()->focusPrev = myNewPrev;
2518 }
2519 }
2520
2521 // Add all children recursively
2522 item->d_ptr->ensureSortedChildren();
2523 for (auto child : std::as_const(item->d_ptr->children))
2524 addItem(child);
2525
2526 // Resolve font and palette.
2527 item->d_ptr->resolveFont(d->font.resolveMask());
2528 item->d_ptr->resolvePalette(d->palette.resolveMask());
2529
2530
2531 // Re-enable selectionChanged() for individual items
2532 --d->selectionChanging;
2533 if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemSize)
2534 emit selectionChanged();
2535
2536 // Deliver post-change notification
2537 item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
2538
2539 // Update explicit activation
2540 bool autoActivate = true;
2541 if (!d->childExplicitActivation && item->d_ptr->explicitActivate)
2542 d->childExplicitActivation = item->d_ptr->wantsActive ? 1 : 2;
2543 if (d->childExplicitActivation && item->isPanel()) {
2544 if (d->childExplicitActivation == 1)
2545 setActivePanel(item);
2546 else
2547 autoActivate = false;
2548 d->childExplicitActivation = 0;
2549 } else if (!item->d_ptr->parent) {
2550 d->childExplicitActivation = 0;
2551 }
2552
2553 // Auto-activate this item's panel if nothing else has been activated
2554 if (autoActivate) {
2555 if (!d->lastActivePanel && !d->activePanel && item->isPanel()) {
2556 if (isActive())
2557 setActivePanel(item);
2558 else
2559 d->lastActivePanel = item;
2560 }
2561 }
2562
2563 if (item->d_ptr->flags & QGraphicsItem::ItemSendsScenePositionChanges)
2564 d->registerScenePosItem(item);
2565
2566 // Ensure that newly added items that have subfocus set, gain
2567 // focus automatically if there isn't a focus item already.
2568 if (!d->focusItem && item != d->lastFocusItem && item->focusItem() == item)
2569 item->focusItem()->setFocus();
2570
2571 d->updateInputMethodSensitivityInViews();
2572}
2573
2574/*!
2575 Creates and adds an ellipse item to the scene, and returns the item
2576 pointer. The geometry of the ellipse is defined by \a rect, and its pen
2577 and brush are initialized to \a pen and \a brush.
2578
2579 Note that the item's geometry is provided in item coordinates, and its
2580 position is initialized to (0, 0).
2581
2582 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2583 QGraphicsScene will emit changed() once control goes back to the event
2584 loop.
2585
2586 \sa addLine(), addPath(), addPixmap(), addRect(), addText(), addItem(),
2587 addWidget()
2588*/
2589QGraphicsEllipseItem *QGraphicsScene::addEllipse(const QRectF &rect, const QPen &pen, const QBrush &brush)
2590{
2591 QGraphicsEllipseItem *item = new QGraphicsEllipseItem(rect);
2592 item->setPen(pen);
2593 item->setBrush(brush);
2594 addItem(item);
2595 return item;
2596}
2597
2598/*!
2599 \fn QGraphicsEllipseItem *QGraphicsScene::addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
2600 \since 4.3
2601
2602 This convenience function is equivalent to calling addEllipse(QRectF(\a x,
2603 \a y, \a w, \a h), \a pen, \a brush).
2604*/
2605
2606/*!
2607 Creates and adds a line item to the scene, and returns the item
2608 pointer. The geometry of the line is defined by \a line, and its pen
2609 is initialized to \a pen.
2610
2611 Note that the item's geometry is provided in item coordinates, and its
2612 position is initialized to (0, 0).
2613
2614 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2615 QGraphicsScene will emit changed() once control goes back to the event
2616 loop.
2617
2618 \sa addEllipse(), addPath(), addPixmap(), addRect(), addText(), addItem(),
2619 addWidget()
2620*/
2621QGraphicsLineItem *QGraphicsScene::addLine(const QLineF &line, const QPen &pen)
2622{
2623 QGraphicsLineItem *item = new QGraphicsLineItem(line);
2624 item->setPen(pen);
2625 addItem(item);
2626 return item;
2627}
2628
2629/*!
2630 \fn QGraphicsLineItem *QGraphicsScene::addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen)
2631 \since 4.3
2632
2633 This convenience function is equivalent to calling addLine(QLineF(\a x1,
2634 \a y1, \a x2, \a y2), \a pen).
2635*/
2636
2637/*!
2638 Creates and adds a path item to the scene, and returns the item
2639 pointer. The geometry of the path is defined by \a path, and its pen and
2640 brush are initialized to \a pen and \a brush.
2641
2642 Note that the item's geometry is provided in item coordinates, and its
2643 position is initialized to (0, 0).
2644
2645 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2646 QGraphicsScene will emit changed() once control goes back to the event
2647 loop.
2648
2649 \sa addEllipse(), addLine(), addPixmap(), addRect(), addText(), addItem(),
2650 addWidget()
2651*/
2652QGraphicsPathItem *QGraphicsScene::addPath(const QPainterPath &path, const QPen &pen, const QBrush &brush)
2653{
2654 QGraphicsPathItem *item = new QGraphicsPathItem(path);
2655 item->setPen(pen);
2656 item->setBrush(brush);
2657 addItem(item);
2658 return item;
2659}
2660
2661/*!
2662 Creates and adds a pixmap item to the scene, and returns the item
2663 pointer. The pixmap is defined by \a pixmap.
2664
2665 Note that the item's geometry is provided in item coordinates, and its
2666 position is initialized to (0, 0).
2667
2668 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2669 QGraphicsScene will emit changed() once control goes back to the event
2670 loop.
2671
2672 \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
2673 addWidget()
2674*/
2675QGraphicsPixmapItem *QGraphicsScene::addPixmap(const QPixmap &pixmap)
2676{
2677 QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
2678 addItem(item);
2679 return item;
2680}
2681
2682/*!
2683 Creates and adds a polygon item to the scene, and returns the item
2684 pointer. The polygon is defined by \a polygon, and its pen and
2685 brush are initialized to \a pen and \a brush.
2686
2687 Note that the item's geometry is provided in item coordinates, and its
2688 position is initialized to (0, 0).
2689
2690 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2691 QGraphicsScene will emit changed() once control goes back to the event
2692 loop.
2693
2694 \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
2695 addWidget()
2696*/
2697QGraphicsPolygonItem *QGraphicsScene::addPolygon(const QPolygonF &polygon,
2698 const QPen &pen, const QBrush &brush)
2699{
2700 QGraphicsPolygonItem *item = new QGraphicsPolygonItem(polygon);
2701 item->setPen(pen);
2702 item->setBrush(brush);
2703 addItem(item);
2704 return item;
2705}
2706
2707/*!
2708 Creates and adds a rectangle item to the scene, and returns the item
2709 pointer. The geometry of the rectangle is defined by \a rect, and its pen
2710 and brush are initialized to \a pen and \a brush.
2711
2712 Note that the item's geometry is provided in item coordinates, and its
2713 position is initialized to (0, 0). For example, if a QRect(50, 50, 100,
2714 100) is added, its top-left corner will be at (50, 50) relative to the
2715 origin in the item's coordinate system.
2716
2717 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2718 QGraphicsScene will emit changed() once control goes back to the event
2719 loop.
2720
2721 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addText(),
2722 addItem(), addWidget()
2723*/
2724QGraphicsRectItem *QGraphicsScene::addRect(const QRectF &rect, const QPen &pen, const QBrush &brush)
2725{
2726 QGraphicsRectItem *item = new QGraphicsRectItem(rect);
2727 item->setPen(pen);
2728 item->setBrush(brush);
2729 addItem(item);
2730 return item;
2731}
2732
2733/*!
2734 \fn QGraphicsRectItem *QGraphicsScene::addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
2735 \since 4.3
2736
2737 This convenience function is equivalent to calling addRect(QRectF(\a x,
2738 \a y, \a w, \a h), \a pen, \a brush).
2739*/
2740
2741/*!
2742 Creates and adds a text item to the scene, and returns the item
2743 pointer. The text string is initialized to \a text, and its font
2744 is initialized to \a font.
2745
2746 The item's position is initialized to (0, 0).
2747
2748 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2749 QGraphicsScene will emit changed() once control goes back to the event
2750 loop.
2751
2752 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
2753 addItem(), addWidget()
2754*/
2755QGraphicsTextItem *QGraphicsScene::addText(const QString &text, const QFont &font)
2756{
2757 QGraphicsTextItem *item = new QGraphicsTextItem(text);
2758 item->setFont(font);
2759 addItem(item);
2760 return item;
2761}
2762
2763/*!
2764 Creates and adds a QGraphicsSimpleTextItem to the scene, and returns the
2765 item pointer. The text string is initialized to \a text, and its font is
2766 initialized to \a font.
2767
2768 The item's position is initialized to (0, 0).
2769
2770 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2771 QGraphicsScene will emit changed() once control goes back to the event
2772 loop.
2773
2774 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
2775 addItem(), addWidget()
2776*/
2777QGraphicsSimpleTextItem *QGraphicsScene::addSimpleText(const QString &text, const QFont &font)
2778{
2779 QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text);
2780 item->setFont(font);
2781 addItem(item);
2782 return item;
2783}
2784
2785/*!
2786 Creates a new QGraphicsProxyWidget for \a widget, adds it to the scene,
2787 and returns a pointer to the proxy. \a wFlags set the default window flags
2788 for the embedding proxy widget.
2789
2790 The item's position is initialized to (0, 0).
2791
2792 If the item is visible (i.e., QGraphicsItem::isVisible() returns \c true),
2793 QGraphicsScene will emit changed() once control goes back to the event
2794 loop.
2795
2796 Note that widgets with the Qt::WA_PaintOnScreen widget attribute
2797 set and widgets that wrap an external application or controller
2798 are not supported. Examples are QOpenGLWidget and QAxWidget.
2799
2800 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
2801 addText(), addSimpleText(), addItem()
2802*/
2803QGraphicsProxyWidget *QGraphicsScene::addWidget(QWidget *widget, Qt::WindowFlags wFlags)
2804{
2805 QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(nullptr, wFlags);
2806 proxy->setWidget(widget);
2807 addItem(proxy);
2808 return proxy;
2809}
2810
2811/*!
2812 Removes the item \a item and all its children from the scene. The
2813 ownership of \a item is passed on to the caller (i.e.,
2814 QGraphicsScene will no longer delete \a item when destroyed).
2815
2816 \sa addItem()
2817*/
2818void QGraphicsScene::removeItem(QGraphicsItem *item)
2819{
2820 // ### Refactoring: This function shares much functionality with _q_removeItemLater()
2821 Q_D(QGraphicsScene);
2822 if (!item) {
2823 qWarning("QGraphicsScene::removeItem: cannot remove 0-item");
2824 return;
2825 }
2826 if (item->scene() != this) {
2827 qWarning("QGraphicsScene::removeItem: item %p's scene (%p)"
2828 " is different from this scene (%p)",
2829 item, item->scene(), this);
2830 return;
2831 }
2832
2833 // Notify the item that it's scene is changing to 0, allowing the item to
2834 // react.
2835 const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
2836 QVariant::fromValue<QGraphicsScene *>(0)));
2837 QGraphicsScene *targetScene = qvariant_cast<QGraphicsScene *>(newSceneVariant);
2838 if (targetScene != nullptr && targetScene != this) {
2839 targetScene->addItem(item);
2840 return;
2841 }
2842
2843 d->removeItemHelper(item);
2844
2845 // Deliver post-change notification
2846 item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
2847
2848 d->updateInputMethodSensitivityInViews();
2849}
2850
2851/*!
2852 When the scene is active, this functions returns the scene's current focus
2853 item, or \nullptr if no item currently has focus. When the scene is inactive,
2854 this functions returns the item that will gain input focus when the scene
2855 becomes active.
2856
2857 The focus item receives keyboard input when the scene receives a
2858 key event.
2859
2860 \sa setFocusItem(), QGraphicsItem::hasFocus(), isActive()
2861*/
2862QGraphicsItem *QGraphicsScene::focusItem() const
2863{
2864 Q_D(const QGraphicsScene);
2865 return isActive() ? d->focusItem : d->passiveFocusItem;
2866}
2867
2868/*!
2869 Sets the scene's focus item to \a item, with the focus reason \a
2870 focusReason, after removing focus from any previous item that may have had
2871 focus.
2872
2873 If \a item is \nullptr, or if it either does not accept focus (i.e., it does not
2874 have the QGraphicsItem::ItemIsFocusable flag enabled), or is not visible
2875 or not enabled, this function only removes focus from any previous
2876 focusitem.
2877
2878 If item is not \nullptr, and the scene does not currently have focus (i.e.,
2879 hasFocus() returns \c false), this function will call setFocus()
2880 automatically.
2881
2882 \sa focusItem(), hasFocus(), setFocus()
2883*/
2884void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason)
2885{
2886 Q_D(QGraphicsScene);
2887 if (item)
2888 item->setFocus(focusReason);
2889 else
2890 d->setFocusItemHelper(item, focusReason);
2891}
2892
2893/*!
2894 Returns \c true if the scene has focus; otherwise returns \c false. If the scene
2895 has focus, it will forward key events from QKeyEvent to any item that
2896 has focus.
2897
2898 \sa setFocus(), setFocusItem()
2899*/
2900bool QGraphicsScene::hasFocus() const
2901{
2902 Q_D(const QGraphicsScene);
2903 return d->hasFocus;
2904}
2905
2906/*!
2907 Sets focus on the scene by sending a QFocusEvent to the scene, passing \a
2908 focusReason as the reason. If the scene regains focus after having
2909 previously lost it while an item had focus, the last focus item will
2910 receive focus with \a focusReason as the reason.
2911
2912 If the scene already has focus, this function does nothing.
2913
2914 \sa hasFocus(), clearFocus(), setFocusItem()
2915*/
2916void QGraphicsScene::setFocus(Qt::FocusReason focusReason)
2917{
2918 Q_D(QGraphicsScene);
2919 if (d->hasFocus || !isActive())
2920 return;
2921 QFocusEvent event(QEvent::FocusIn, focusReason);
2922 QCoreApplication::sendEvent(this, &event);
2923}
2924
2925/*!
2926 Clears focus from the scene. If any item has focus when this function is
2927 called, it will lose focus, and regain focus again once the scene regains
2928 focus.
2929
2930 A scene that does not have focus ignores key events.
2931
2932 \sa hasFocus(), setFocus(), setFocusItem()
2933*/
2934void QGraphicsScene::clearFocus()
2935{
2936 Q_D(QGraphicsScene);
2937 if (d->hasFocus) {
2938 d->hasFocus = false;
2939 d->passiveFocusItem = d->focusItem;
2940 setFocusItem(nullptr, Qt::OtherFocusReason);
2941 }
2942}
2943
2944/*!
2945 \property QGraphicsScene::stickyFocus
2946 \brief whether clicking into the scene background will clear focus
2947
2948 \since 4.6
2949
2950 In a QGraphicsScene with stickyFocus set to true, focus will remain
2951 unchanged when the user clicks into the scene background or on an item
2952 that does not accept focus. Otherwise, focus will be cleared.
2953
2954 By default, this property is \c false.
2955
2956 Focus changes in response to a mouse press. You can reimplement
2957 mousePressEvent() in a subclass of QGraphicsScene to toggle this property
2958 based on where the user has clicked.
2959
2960 \sa clearFocus(), setFocusItem()
2961*/
2962void QGraphicsScene::setStickyFocus(bool enabled)
2963{
2964 Q_D(QGraphicsScene);
2965 d->stickyFocus = enabled;
2966}
2967bool QGraphicsScene::stickyFocus() const
2968{
2969 Q_D(const QGraphicsScene);
2970 return d->stickyFocus;
2971}
2972
2973/*!
2974 Returns the current mouse grabber item, or \nullptr if no item is
2975 currently grabbing the mouse. The mouse grabber item is the item
2976 that receives all mouse events sent to the scene.
2977
2978 An item becomes a mouse grabber when it receives and accepts a
2979 mouse press event, and it stays the mouse grabber until either of
2980 the following events occur:
2981
2982 \list
2983 \li If the item receives a mouse release event when there are no other
2984 buttons pressed, it loses the mouse grab.
2985 \li If the item becomes invisible (i.e., someone calls \c {item->setVisible(false)}),
2986 or if it becomes disabled (i.e., someone calls \c {item->setEnabled(false)}),
2987 it loses the mouse grab.
2988 \li If the item is removed from the scene, it loses the mouse grab.
2989 \endlist
2990
2991 If the item loses its mouse grab, the scene will ignore all mouse events
2992 until a new item grabs the mouse (i.e., until a new item receives a mouse
2993 press event).
2994*/
2995QGraphicsItem *QGraphicsScene::mouseGrabberItem() const
2996{
2997 Q_D(const QGraphicsScene);
2998 return !d->mouseGrabberItems.isEmpty() ? d->mouseGrabberItems.last() : 0;
2999}
3000
3001/*!
3002 \property QGraphicsScene::backgroundBrush
3003 \brief the background brush of the scene.
3004
3005 Set this property to changes the scene's background to a different color,
3006 gradient or texture. The default background brush is Qt::NoBrush. The
3007 background is drawn before (behind) the items.
3008
3009 Example:
3010
3011 \snippet code/src_gui_graphicsview_qgraphicsscene.cpp 3
3012
3013 QGraphicsScene::render() calls drawBackground() to draw the scene
3014 background. For more detailed control over how the background is drawn,
3015 you can reimplement drawBackground() in a subclass of QGraphicsScene.
3016*/
3017QBrush QGraphicsScene::backgroundBrush() const
3018{
3019 Q_D(const QGraphicsScene);
3020 return d->backgroundBrush;
3021}
3022void QGraphicsScene::setBackgroundBrush(const QBrush &brush)
3023{
3024 Q_D(QGraphicsScene);
3025 d->backgroundBrush = brush;
3026 for (QGraphicsView *view : std::as_const(d->views)) {
3027 view->resetCachedContent();
3028 view->viewport()->update();
3029 }
3030 update();
3031}
3032
3033/*!
3034 \property QGraphicsScene::foregroundBrush
3035 \brief the foreground brush of the scene.
3036
3037 Change this property to set the scene's foreground to a different
3038 color, gradient or texture.
3039
3040 The foreground is drawn after (on top of) the items. The default
3041 foreground brush is Qt::NoBrush ( i.e. the foreground is not
3042 drawn).
3043
3044 Example:
3045
3046 \snippet code/src_gui_graphicsview_qgraphicsscene.cpp 4
3047
3048 QGraphicsScene::render() calls drawForeground() to draw the scene
3049 foreground. For more detailed control over how the foreground is
3050 drawn, you can reimplement the drawForeground() function in a
3051 QGraphicsScene subclass.
3052*/
3053QBrush QGraphicsScene::foregroundBrush() const
3054{
3055 Q_D(const QGraphicsScene);
3056 return d->foregroundBrush;
3057}
3058void QGraphicsScene::setForegroundBrush(const QBrush &brush)
3059{
3060 Q_D(QGraphicsScene);
3061 d->foregroundBrush = brush;
3062 const auto views_ = views();
3063 for (QGraphicsView *view : views_)
3064 view->viewport()->update();
3065 update();
3066}
3067
3068/*!
3069 This method is used by input methods to query a set of properties of
3070 the scene to be able to support complex input method operations as support
3071 for surrounding text and reconversions.
3072
3073 The \a query parameter specifies which property is queried.
3074
3075 \sa QWidget::inputMethodQuery()
3076*/
3077QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const
3078{
3079 Q_D(const QGraphicsScene);
3080 if (!d->focusItem || !(d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod))
3081 return QVariant();
3082 const QTransform matrix = d->focusItem->sceneTransform();
3083 QVariant value = d->focusItem->inputMethodQuery(query);
3084 if (value.userType() == QMetaType::QRectF)
3085 value = matrix.mapRect(value.toRectF());
3086 else if (value.userType() == QMetaType::QPointF)
3087 value = matrix.map(value.toPointF());
3088 else if (value.userType() == QMetaType::QRect)
3089 value = matrix.mapRect(value.toRect());
3090 else if (value.userType() == QMetaType::QPoint)
3091 value = matrix.map(value.toPoint());
3092 return value;
3093}
3094
3095/*!
3096 \fn void QGraphicsScene::update(const QRectF &rect)
3097 Schedules a redraw of the area \a rect on the scene.
3098
3099 \sa sceneRect(), changed()
3100*/
3101void QGraphicsScene::update(const QRectF &rect)
3102{
3103 Q_D(QGraphicsScene);
3104 if (d->updateAll || (rect.isEmpty() && !rect.isNull()))
3105 return;
3106
3107 // Check if anyone's connected; if not, we can send updates directly to
3108 // the views. Otherwise or if there are no views, use old behavior.
3109 bool directUpdates = !(d->isSignalConnected(d->changedSignalIndex)) && !d->views.isEmpty();
3110 if (rect.isNull()) {
3111 d->updateAll = true;
3112 d->updatedRects.clear();
3113 if (directUpdates) {
3114 // Update all views.
3115 for (auto view : std::as_const(d->views))
3116 view->d_func()->fullUpdatePending = true;
3117 }
3118 } else {
3119 if (directUpdates) {
3120 // Update all views.
3121 for (auto view : std::as_const(d->views)) {
3122 if (view->isTransformed())
3123 view->d_func()->updateRectF(view->viewportTransform().mapRect(rect));
3124 else
3125 view->d_func()->updateRectF(rect);
3126 }
3127 } else {
3128 d->updatedRects.insert(rect);
3129 }
3130 }
3131
3132 if (!d->calledEmitUpdated) {
3133 d->calledEmitUpdated = true;
3134 QMetaObject::invokeMethod(this, "_q_emitUpdated", Qt::QueuedConnection);
3135 }
3136}
3137
3138/*!
3139 \fn void QGraphicsScene::update(qreal x, qreal y, qreal w, qreal h)
3140 \overload
3141 \since 4.3
3142
3143 This function is equivalent to calling update(QRectF(\a x, \a y, \a w,
3144 \a h));
3145*/
3146
3147/*!
3148 Invalidates and schedules a redraw of the \a layers in \a rect on the
3149 scene. Any cached content in \a layers is unconditionally invalidated and
3150 redrawn.
3151
3152 You can use this function overload to notify QGraphicsScene of changes to
3153 the background or the foreground of the scene. This function is commonly
3154 used for scenes with tile-based backgrounds to notify changes when
3155 QGraphicsView has enabled
3156 \l{QGraphicsView::CacheBackground}{CacheBackground}.
3157
3158 Example:
3159
3160 \snippet code/src_gui_graphicsview_qgraphicsscene.cpp 5
3161
3162 Note that QGraphicsView currently supports background caching only (see
3163 QGraphicsView::CacheBackground). This function is equivalent to calling
3164 update() if any layer but BackgroundLayer is passed.
3165
3166 \sa QGraphicsView::resetCachedContent()
3167*/
3168void QGraphicsScene::invalidate(const QRectF &rect, SceneLayers layers)
3169{
3170 const auto views_ = views();
3171 for (QGraphicsView *view : views_)
3172 view->invalidateScene(rect, layers);
3173 update(rect);
3174}
3175
3176/*!
3177 \fn void QGraphicsScene::invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers)
3178 \overload
3179 \since 4.3
3180
3181 This convenience function is equivalent to calling invalidate(QRectF(\a x, \a
3182 y, \a w, \a h), \a layers);
3183*/
3184
3185/*!
3186 Returns a list of all the views that display this scene.
3187
3188 \sa QGraphicsView::scene()
3189*/
3190QList <QGraphicsView *> QGraphicsScene::views() const
3191{
3192 Q_D(const QGraphicsScene);
3193 return d->views;
3194}
3195
3196/*!
3197 This slot \e advances the scene by one step, by calling
3198 QGraphicsItem::advance() for all items on the scene. This is done in two
3199 phases: in the first phase, all items are notified that the scene is about
3200 to change, and in the second phase all items are notified that they can
3201 move. In the first phase, QGraphicsItem::advance() is called passing a
3202 value of 0 as an argument, and 1 is passed in the second phase.
3203
3204 Note that you can also use the \l{The Animation Framework}{Animation
3205 Framework} for animations.
3206
3207 \sa QGraphicsItem::advance(), QTimeLine
3208*/
3209void QGraphicsScene::advance()
3210{
3211 for (int i = 0; i < 2; ++i) {
3212 const auto items_ = items();
3213 for (QGraphicsItem *item : items_)
3214 item->advance(i);
3215 }
3216}
3217
3218/*!
3219 Processes the event \a event, and dispatches it to the respective
3220 event handlers.
3221
3222 In addition to calling the convenience event handlers, this
3223 function is responsible for converting mouse move events to hover
3224 events for when there is no mouse grabber item. Hover events are
3225 delivered directly to items; there is no convenience function for
3226 them.
3227
3228 Unlike QWidget, QGraphicsScene does not have the convenience functions
3229 \l{QWidget::}{enterEvent()} and \l{QWidget::}{leaveEvent()}. Use this
3230 function to obtain those events instead.
3231
3232 Returns \c true if \a event has been recognized and processed; otherwise,
3233 returns \c false.
3234
3235 \sa contextMenuEvent(), keyPressEvent(), keyReleaseEvent(),
3236 mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(),
3237 mouseDoubleClickEvent(), focusInEvent(), focusOutEvent()
3238*/
3239bool QGraphicsScene::event(QEvent *event)
3240{
3241 Q_D(QGraphicsScene);
3242
3243 switch (event->type()) {
3244 case QEvent::GraphicsSceneMousePress:
3245 case QEvent::GraphicsSceneMouseMove:
3246 case QEvent::GraphicsSceneMouseRelease:
3247 case QEvent::GraphicsSceneMouseDoubleClick:
3248 case QEvent::GraphicsSceneHoverEnter:
3249 case QEvent::GraphicsSceneHoverLeave:
3250 case QEvent::GraphicsSceneHoverMove:
3251 case QEvent::TouchBegin:
3252 case QEvent::TouchUpdate:
3253 case QEvent::TouchEnd:
3254 // Reset the under-mouse list to ensure that this event gets fresh
3255 // item-under-mouse data. Be careful about this list; if people delete
3256 // items from inside event handlers, this list can quickly end up
3257 // having stale pointers in it. We need to clear it before dispatching
3258 // events that use it.
3259 // ### this should only be cleared if we received a new mouse move event,
3260 // which relies on us fixing the replay mechanism in QGraphicsView.
3261 d->cachedItemsUnderMouse.clear();
3262 break;
3263 default:
3264 break;
3265 }
3266
3267 switch (event->type()) {
3268 case QEvent::GraphicsSceneDragEnter:
3269 dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3270 break;
3271 case QEvent::GraphicsSceneDragMove:
3272 dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3273 break;
3274 case QEvent::GraphicsSceneDragLeave:
3275 dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3276 break;
3277 case QEvent::GraphicsSceneDrop:
3278 dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3279 break;
3280 case QEvent::GraphicsSceneContextMenu:
3281 contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event));
3282 break;
3283 case QEvent::KeyPress:
3284 if (!d->focusItem) {
3285 QKeyEvent *k = static_cast<QKeyEvent *>(event);
3286 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
3287 if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
3288 bool res = false;
3289 if (k->key() == Qt::Key_Backtab
3290 || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) {
3291 res = focusNextPrevChild(false);
3292 } else if (k->key() == Qt::Key_Tab) {
3293 res = focusNextPrevChild(true);
3294 }
3295 if (!res)
3296 event->ignore();
3297 return true;
3298 }
3299 }
3300 }
3301 keyPressEvent(static_cast<QKeyEvent *>(event));
3302 break;
3303 case QEvent::KeyRelease:
3304 keyReleaseEvent(static_cast<QKeyEvent *>(event));
3305 break;
3306 case QEvent::ShortcutOverride: {
3307 QGraphicsItem *parent = focusItem();
3308 while (parent) {
3309 d->sendEvent(parent, event);
3310 if (event->isAccepted())
3311 return true;
3312 parent = parent->parentItem();
3313 }
3314 }
3315 return false;
3316 case QEvent::GraphicsSceneMouseMove:
3317 {
3318 QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
3319 d->lastSceneMousePos = mouseEvent->scenePos();
3320 mouseMoveEvent(mouseEvent);
3321 break;
3322 }
3323 case QEvent::GraphicsSceneMousePress:
3324 mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
3325 break;
3326 case QEvent::GraphicsSceneMouseRelease:
3327 mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
3328 break;
3329 case QEvent::GraphicsSceneMouseDoubleClick:
3330 mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
3331 break;
3332 case QEvent::GraphicsSceneWheel:
3333 wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
3334 break;
3335 case QEvent::FocusIn:
3336 focusInEvent(static_cast<QFocusEvent *>(event));
3337 break;
3338 case QEvent::FocusOut:
3339 focusOutEvent(static_cast<QFocusEvent *>(event));
3340 break;
3341 case QEvent::GraphicsSceneHoverEnter:
3342 case QEvent::GraphicsSceneHoverLeave:
3343 case QEvent::GraphicsSceneHoverMove:
3344 {
3345 QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event);
3346 d->lastSceneMousePos = hoverEvent->scenePos();
3347 d->dispatchHoverEvent(hoverEvent);
3348 break;
3349 }
3350 case QEvent::Leave:
3351 Q_ASSERT_X(false, "QGraphicsScene::event",
3352 "QGraphicsScene must not receive QEvent::Leave, use GraphicsSceneLeave");
3353 break;
3354 case QEvent::GraphicsSceneLeave:
3355 {
3356 auto *leaveEvent = static_cast<QGraphicsSceneEvent*>(event);
3357 d->leaveScene(leaveEvent->widget());
3358 break;
3359 }
3360 case QEvent::GraphicsSceneHelp:
3361 helpEvent(static_cast<QGraphicsSceneHelpEvent *>(event));
3362 break;
3363 case QEvent::InputMethod:
3364 inputMethodEvent(static_cast<QInputMethodEvent *>(event));
3365 break;
3366 case QEvent::WindowActivate:
3367 if (!d->activationRefCount++) {
3368 if (d->lastActivePanel) {
3369 // Activate the last panel.
3370 d->setActivePanelHelper(d->lastActivePanel, true);
3371 } else if (d->tabFocusFirst && d->tabFocusFirst->isPanel()) {
3372 // Activate the panel of the first item in the tab focus
3373 // chain.
3374 d->setActivePanelHelper(d->tabFocusFirst, true);
3375 } else {
3376 // Activate all toplevel items.
3377 QEvent event(QEvent::WindowActivate);
3378 const auto items_ = items();
3379 for (QGraphicsItem *item : items_) {
3380 if (item->isVisible() && !item->isPanel() && !item->parentItem())
3381 sendEvent(item, &event);
3382 }
3383 }
3384 }
3385 break;
3386 case QEvent::WindowDeactivate:
3387 if (!--d->activationRefCount) {
3388 if (d->activePanel) {
3389 // Deactivate the active panel (but keep it so we can
3390 // reactivate it later).
3391 QGraphicsItem *lastActivePanel = d->activePanel;
3392 d->setActivePanelHelper(nullptr, true);
3393 d->lastActivePanel = lastActivePanel;
3394 } else {
3395 // Activate all toplevel items.
3396 QEvent event(QEvent::WindowDeactivate);
3397 const auto items_ = items();
3398 for (QGraphicsItem *item : items_) {
3399 if (item->isVisible() && !item->isPanel() && !item->parentItem())
3400 sendEvent(item, &event);
3401 }
3402 }
3403 }
3404 break;
3405 case QEvent::ApplicationFontChange: {
3406 // Resolve the existing scene font.
3407 d->resolveFont();
3408 break;
3409 }
3410 case QEvent::FontChange:
3411 // Update the entire scene when the font changes.
3412 update();
3413 break;
3414 case QEvent::ApplicationPaletteChange: {
3415 // Resolve the existing scene palette.
3416 d->resolvePalette();
3417 break;
3418 }
3419 case QEvent::PaletteChange:
3420 // Update the entire scene when the palette changes.
3421 update();
3422 break;
3423 case QEvent::StyleChange:
3424 // Reresolve all widgets' styles. Update all top-level widgets'
3425 // geometries that do not have an explicit style set.
3426 update();
3427 break;
3428 case QEvent::StyleAnimationUpdate:
3429 // Because QGraphicsItem is not a QObject, QStyle driven
3430 // animations are forced to update the whole scene
3431 update();
3432 break;
3433 case QEvent::TouchBegin:
3434 case QEvent::TouchUpdate:
3435 case QEvent::TouchEnd:
3436 d->touchEventHandler(static_cast<QTouchEvent *>(event));
3437 break;
3438#ifndef QT_NO_GESTURES
3439 case QEvent::Gesture:
3440 case QEvent::GestureOverride:
3441 d->gestureEventHandler(static_cast<QGestureEvent *>(event));
3442 break;
3443#endif // QT_NO_GESTURES
3444 default:
3445 return QObject::event(event);
3446 }
3447 return true;
3448}
3449
3450/*!
3451 \reimp
3452
3453 QGraphicsScene filters QApplication's events to detect palette and font
3454 changes.
3455*/
3456bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event)
3457{
3458 if (watched != qApp)
3459 return false;
3460
3461 switch (event->type()) {
3462 case QEvent::ApplicationPaletteChange:
3463 QCoreApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange));
3464 break;
3465 case QEvent::ApplicationFontChange:
3466 QCoreApplication::postEvent(this, new QEvent(QEvent::ApplicationFontChange));
3467 break;
3468 default:
3469 break;
3470 }
3471 return false;
3472}
3473
3474/*!
3475 This event handler, for event \a contextMenuEvent, can be reimplemented in
3476 a subclass to receive context menu events. The default implementation
3477 forwards the event to the topmost visible item that accepts context menu events at
3478 the position of the event. If no items accept context menu events at this
3479 position, the event is ignored.
3480
3481 Note: See items() for a definition of which items are considered visible by this function.
3482
3483 \sa QGraphicsItem::contextMenuEvent()
3484*/
3485void QGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent)
3486{
3487 Q_D(QGraphicsScene);
3488 // Ignore by default.
3489 contextMenuEvent->ignore();
3490
3491 // Send the event to all items at this position until one item accepts the
3492 // event.
3493 const auto items = d->itemsAtPosition(contextMenuEvent->screenPos(),
3494 contextMenuEvent->scenePos(),
3495 contextMenuEvent->widget());
3496 for (QGraphicsItem *item : items) {
3497 contextMenuEvent->setPos(item->d_ptr->genericMapFromScene(contextMenuEvent->scenePos(),
3498 contextMenuEvent->widget()));
3499 contextMenuEvent->accept();
3500 if (!d->sendEvent(item, contextMenuEvent))
3501 break;
3502
3503 if (contextMenuEvent->isAccepted())
3504 break;
3505 }
3506}
3507
3508/*!
3509 This event handler, for event \a event, can be reimplemented in a subclass
3510 to receive drag enter events for the scene.
3511
3512 The default implementation accepts the event and prepares the scene to
3513 accept drag move events.
3514
3515 \sa QGraphicsItem::dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(),
3516 dropEvent()
3517*/
3518void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
3519{
3520 Q_D(QGraphicsScene);
3521 d->dragDropItem = nullptr;
3522 d->lastDropAction = Qt::IgnoreAction;
3523 event->accept();
3524}
3525
3526/*!
3527 This event handler, for event \a event, can be reimplemented in a subclass
3528 to receive drag move events for the scene.
3529
3530 Note: See items() for a definition of which items are considered visible by this function.
3531
3532 \sa QGraphicsItem::dragMoveEvent(), dragEnterEvent(), dragLeaveEvent(),
3533 dropEvent()
3534*/
3535void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
3536{
3537 Q_D(QGraphicsScene);
3538 event->ignore();
3539
3540 if (!d->mouseGrabberItems.isEmpty()) {
3541 // Mouse grabbers that start drag events lose the mouse grab.
3542 d->clearMouseGrabber();
3543 d->mouseGrabberButtonDownPos.clear();
3544 d->mouseGrabberButtonDownScenePos.clear();
3545 d->mouseGrabberButtonDownScreenPos.clear();
3546 }
3547
3548 bool eventDelivered = false;
3549
3550 // Find the topmost enabled items under the cursor. They are all
3551 // candidates for accepting drag & drop events.
3552 const auto items = d->itemsAtPosition(event->screenPos(),
3553 event->scenePos(),
3554 event->widget());
3555 for (QGraphicsItem *item : items) {
3556 if (!item->isEnabled() || !item->acceptDrops())
3557 continue;
3558
3559 if (item != d->dragDropItem) {
3560 // Enter the new drag drop item. If it accepts the event, we send
3561 // the leave to the parent item.
3562 QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter);
3563 d->cloneDragDropEvent(&dragEnter, event);
3564 dragEnter.setDropAction(event->proposedAction());
3565 d->sendDragDropEvent(item, &dragEnter);
3566 event->setAccepted(dragEnter.isAccepted());
3567 event->setDropAction(dragEnter.dropAction());
3568 if (!event->isAccepted()) {
3569 // Propagate to the item under
3570 continue;
3571 }
3572
3573 d->lastDropAction = event->dropAction();
3574
3575 if (d->dragDropItem) {
3576 // Leave the last drag drop item. A perfect implementation
3577 // would set the position of this event to the point where
3578 // this event and the last event intersect with the item's
3579 // shape, but that's not easy to do. :-)
3580 QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
3581 d->cloneDragDropEvent(&dragLeave, event);
3582 d->sendDragDropEvent(d->dragDropItem, &dragLeave);
3583 }
3584
3585 // We've got a new drag & drop item
3586 d->dragDropItem = item;
3587 }
3588
3589 // Send the move event.
3590 event->setDropAction(d->lastDropAction);
3591 event->accept();
3592 d->sendDragDropEvent(item, event);
3593 if (event->isAccepted())
3594 d->lastDropAction = event->dropAction();
3595 eventDelivered = true;
3596 break;
3597 }
3598
3599 if (!eventDelivered) {
3600 if (d->dragDropItem) {
3601 // Leave the last drag drop item
3602 QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
3603 d->cloneDragDropEvent(&dragLeave, event);
3604 d->sendDragDropEvent(d->dragDropItem, &dragLeave);
3605 d->dragDropItem = nullptr;
3606 }
3607 // Propagate
3608 event->setDropAction(Qt::IgnoreAction);
3609 }
3610}
3611
3612/*!
3613 This event handler, for event \a event, can be reimplemented in a subclass
3614 to receive drag leave events for the scene.
3615
3616 \sa QGraphicsItem::dragLeaveEvent(), dragEnterEvent(), dragMoveEvent(),
3617 dropEvent()
3618*/
3619void QGraphicsScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
3620{
3621 Q_D(QGraphicsScene);
3622 if (d->dragDropItem) {
3623 // Leave the last drag drop item
3624 d->sendDragDropEvent(d->dragDropItem, event);
3625 d->dragDropItem = nullptr;
3626 }
3627}
3628
3629/*!
3630 This event handler, for event \a event, can be reimplemented in a subclass
3631 to receive drop events for the scene.
3632
3633 \sa QGraphicsItem::dropEvent(), dragEnterEvent(), dragMoveEvent(),
3634 dragLeaveEvent()
3635*/
3636void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
3637{
3638 Q_UNUSED(event);
3639 Q_D(QGraphicsScene);
3640 if (d->dragDropItem) {
3641 // Drop on the last drag drop item
3642 d->sendDragDropEvent(d->dragDropItem, event);
3643 d->dragDropItem = nullptr;
3644 }
3645}
3646
3647/*!
3648 This event handler, for event \a focusEvent, can be reimplemented in a
3649 subclass to receive focus in events.
3650
3651 The default implementation sets focus on the scene, and then on the last
3652 focus item.
3653
3654 \sa QGraphicsItem::focusOutEvent()
3655*/
3656void QGraphicsScene::focusInEvent(QFocusEvent *focusEvent)
3657{
3658 Q_D(QGraphicsScene);
3659
3660 d->hasFocus = true;
3661 switch (focusEvent->reason()) {
3662 case Qt::TabFocusReason:
3663 if (!focusNextPrevChild(true))
3664 focusEvent->ignore();
3665 break;
3666 case Qt::BacktabFocusReason:
3667 if (!focusNextPrevChild(false))
3668 focusEvent->ignore();
3669 break;
3670 default:
3671 if (d->passiveFocusItem) {
3672 // Set focus on the last focus item
3673 setFocusItem(d->passiveFocusItem, focusEvent->reason());
3674 }
3675 break;
3676 }
3677}
3678
3679/*!
3680 This event handler, for event \a focusEvent, can be reimplemented in a
3681 subclass to receive focus out events.
3682
3683 The default implementation removes focus from any focus item, then removes
3684 focus from the scene.
3685
3686 \sa QGraphicsItem::focusInEvent()
3687*/
3688void QGraphicsScene::focusOutEvent(QFocusEvent *focusEvent)
3689{
3690 Q_D(QGraphicsScene);
3691 d->hasFocus = false;
3692 d->passiveFocusItem = d->focusItem;
3693 setFocusItem(nullptr, focusEvent->reason());
3694
3695 // Remove all popups when the scene loses focus.
3696 if (!d->popupWidgets.isEmpty())
3697 d->removePopup(d->popupWidgets.constFirst());
3698}
3699
3700/*!
3701 This event handler, for event \a helpEvent, can be
3702 reimplemented in a subclass to receive help events. The events
3703 are of type QEvent::ToolTip, which are created when a tooltip is
3704 requested.
3705
3706 The default implementation shows the tooltip of the topmost
3707 visible item, i.e., the item with the highest z-value, at the mouse
3708 cursor position. If no item has a tooltip set, this function
3709 does nothing.
3710
3711 Note: See items() for a definition of which items are considered visible by this function.
3712
3713 \sa QGraphicsItem::toolTip(), QGraphicsSceneHelpEvent
3714*/
3715void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent)
3716{
3717#if !QT_CONFIG(tooltip)
3718 Q_UNUSED(helpEvent);
3719#else
3720 // Find the first item that does tooltips
3721 Q_D(QGraphicsScene);
3722 const QList<QGraphicsItem *> itemsAtPos = d->itemsAtPosition(helpEvent->screenPos(),
3723 helpEvent->scenePos(),
3724 helpEvent->widget());
3725 QGraphicsItem *toolTipItem = nullptr;
3726 for (auto item : itemsAtPos) {
3727 if (item->d_func()->isProxyWidget()) {
3728 // if the item is a proxy widget, the event is forwarded to it
3729 sendEvent(item, helpEvent);
3730 if (helpEvent->isAccepted())
3731 return;
3732 }
3733 if (!item->toolTip().isEmpty()) {
3734 toolTipItem = item;
3735 break;
3736 }
3737 }
3738
3739 // Show or hide the tooltip
3740 QString text;
3741 QPoint point;
3742 if (toolTipItem && !toolTipItem->toolTip().isEmpty()) {
3743 text = toolTipItem->toolTip();
3744 point = helpEvent->screenPos();
3745 }
3746 QToolTip::showText(point, text, helpEvent->widget());
3747 helpEvent->setAccepted(!text.isEmpty());
3748#endif
3749}
3750
3751bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const
3752{
3753 return (item->d_ptr->acceptsHover
3754 || (item->d_ptr->isWidget
3755 && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration()))
3756 && !item->isBlockedByModalPanel();
3757}
3758
3759/*!
3760 This event handler, for event \a hoverEvent, can be reimplemented in a
3761 subclass to receive hover enter events. The default implementation
3762 forwards the event to the topmost visible item that accepts hover events at the
3763 scene position from the event.
3764
3765 Note: See items() for a definition of which items are considered visible by this function.
3766
3767 \sa QGraphicsItem::hoverEvent(), QGraphicsItem::setAcceptHoverEvents()
3768*/
3769bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent)
3770{
3771 if (allItemsIgnoreHoverEvents)
3772 return false;
3773
3774 // Find the first item that accepts hover events, reusing earlier
3775 // calculated data is possible.
3776 if (cachedItemsUnderMouse.isEmpty()) {
3777 cachedItemsUnderMouse = itemsAtPosition(hoverEvent->screenPos(),
3778 hoverEvent->scenePos(),
3779 hoverEvent->widget());
3780 }
3781
3782 QGraphicsItem *item = nullptr;
3783 for (auto tmp : std::as_const(cachedItemsUnderMouse)) {
3784 if (itemAcceptsHoverEvents_helper(tmp)) {
3785 item = tmp;
3786 break;
3787 }
3788 }
3789
3790 // Find the common ancestor item for the new topmost hoverItem and the
3791 // last item in the hoverItem list.
3792 QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.constLast()) : nullptr;
3793 while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem))
3794 commonAncestorItem = commonAncestorItem->parentItem();
3795 if (commonAncestorItem && commonAncestorItem->panel() != item->panel()) {
3796 // The common ancestor isn't in the same panel as the two hovered
3797 // items.
3798 commonAncestorItem = nullptr;
3799 }
3800
3801 // Check if the common ancestor item is known.
3802 int index = commonAncestorItem ? hoverItems.indexOf(commonAncestorItem) : -1;
3803 // Send hover leaves to any existing hovered children of the common
3804 // ancestor item.
3805 for (int i = hoverItems.size() - 1; i > index; --i) {
3806 QGraphicsItem *lastItem = hoverItems.takeLast();
3807 if (itemAcceptsHoverEvents_helper(lastItem))
3808 sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, hoverEvent);
3809 }
3810
3811 // Item is a child of a known item. Generate enter events for the
3812 // missing links.
3813 QList<QGraphicsItem *> parents;
3814 QGraphicsItem *parent = item;
3815 while (parent && parent != commonAncestorItem) {
3816 parents.append(parent);
3817 if (parent->isPanel()) {
3818 // Stop at the panel - we don't deliver beyond this point.
3819 break;
3820 }
3821 parent = parent->parentItem();
3822 }
3823 for (auto it = parents.crbegin(), end = parents.crend(); it != end; ++it) {
3824 QGraphicsItem *parent = *it;
3825 hoverItems << parent;
3826 if (itemAcceptsHoverEvents_helper(parent))
3827 sendHoverEvent(QEvent::GraphicsSceneHoverEnter, parent, hoverEvent);
3828 }
3829
3830 // Generate a move event for the item itself
3831 if (item
3832 && !hoverItems.isEmpty()
3833 && item == hoverItems.constLast()) {
3834 sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, hoverEvent);
3835 return true;
3836 }
3837 return false;
3838}
3839
3840/*!
3841 \internal
3842
3843 Handles all actions necessary to clean up the scene when the mouse leaves
3844 the view.
3845*/
3846void QGraphicsScenePrivate::leaveScene(QWidget *viewport)
3847{
3848#if QT_CONFIG(tooltip)
3849 QToolTip::hideText();
3850#endif
3851 QGraphicsView *view = qobject_cast<QGraphicsView *>(viewport->parent());
3852 // Send HoverLeave events to all existing hover items, topmost first.
3853 QGraphicsSceneHoverEvent hoverEvent;
3854 hoverEvent.setWidget(viewport);
3855
3856 if (view) {
3857 QPoint cursorPos = QCursor::pos();
3858 hoverEvent.setScenePos(view->mapToScene(viewport->mapFromGlobal(cursorPos)));
3859 hoverEvent.setLastScenePos(hoverEvent.scenePos());
3860 hoverEvent.setScreenPos(cursorPos);
3861 hoverEvent.setLastScreenPos(hoverEvent.screenPos());
3862 }
3863
3864 while (!hoverItems.isEmpty()) {
3865 QGraphicsItem *lastItem = hoverItems.takeLast();
3866 if (itemAcceptsHoverEvents_helper(lastItem))
3867 sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, &hoverEvent);
3868 }
3869}
3870
3871/*!
3872 This event handler, for event \a keyEvent, can be reimplemented in a
3873 subclass to receive keypress events. The default implementation forwards
3874 the event to current focus item.
3875
3876 \sa QGraphicsItem::keyPressEvent(), focusItem()
3877*/
3878void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent)
3879{
3880 // ### Merge this function with keyReleaseEvent; they are identical
3881 // ### (except this comment).
3882 Q_D(QGraphicsScene);
3883 QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.constLast() : 0;
3884 if (!item)
3885 item = focusItem();
3886 if (item) {
3887 QGraphicsItem *p = item;
3888 do {
3889 // Accept the event by default
3890 keyEvent->accept();
3891 // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
3892 // is filtered out, stop propagating it.
3893 if (p->isBlockedByModalPanel())
3894 break;
3895 if (!d->sendEvent(p, keyEvent))
3896 break;
3897 } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem()));
3898 } else {
3899 keyEvent->ignore();
3900 }
3901}
3902
3903/*!
3904 This event handler, for event \a keyEvent, can be reimplemented in a
3905 subclass to receive key release events. The default implementation
3906 forwards the event to current focus item.
3907
3908 \sa QGraphicsItem::keyReleaseEvent(), focusItem()
3909*/
3910void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
3911{
3912 // ### Merge this function with keyPressEvent; they are identical (except
3913 // ### this comment).
3914 Q_D(QGraphicsScene);
3915 QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.constLast() : 0;
3916 if (!item)
3917 item = focusItem();
3918 if (item) {
3919 QGraphicsItem *p = item;
3920 do {
3921 // Accept the event by default
3922 keyEvent->accept();
3923 // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
3924 // is filtered out, stop propagating it.
3925 if (p->isBlockedByModalPanel())
3926 break;
3927 if (!d->sendEvent(p, keyEvent))
3928 break;
3929 } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem()));
3930 } else {
3931 keyEvent->ignore();
3932 }
3933}
3934
3935/*!
3936 This event handler, for event \a mouseEvent, can be reimplemented
3937 in a subclass to receive mouse press events for the scene.
3938
3939 The default implementation depends on the state of the scene. If
3940 there is a mouse grabber item, then the event is sent to the mouse
3941 grabber. Otherwise, it is forwarded to the topmost visible item that
3942 accepts mouse events at the scene position from the event, and
3943 that item promptly becomes the mouse grabber item.
3944
3945 If there is no item at the given position on the scene, the
3946 selection area is reset, any focus item loses its input focus, and
3947 the event is then ignored.
3948
3949 Note: See items() for a definition of which items are considered visible by this function.
3950
3951 \sa QGraphicsItem::mousePressEvent(),
3952 QGraphicsItem::setAcceptedMouseButtons()
3953*/
3954void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
3955{
3956 Q_D(QGraphicsScene);
3957 if (d->mouseGrabberItems.isEmpty()) {
3958 // Dispatch hover events
3959 QGraphicsSceneHoverEvent hover;
3960 _q_hoverFromMouseEvent(&hover, mouseEvent);
3961 d->dispatchHoverEvent(&hover);
3962 }
3963
3964 d->mousePressEventHandler(mouseEvent);
3965}
3966
3967/*!
3968 This event handler, for event \a mouseEvent, can be reimplemented
3969 in a subclass to receive mouse move events for the scene.
3970
3971 The default implementation depends on the mouse grabber state. If there is
3972 a mouse grabber item, the event is sent to the mouse grabber. If there
3973 are any items that accept hover events at the current position, the event
3974 is translated into a hover event and accepted; otherwise it's ignored.
3975
3976 \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseReleaseEvent(),
3977 QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
3978*/
3979void QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
3980{
3981 Q_D(QGraphicsScene);
3982 if (d->mouseGrabberItems.isEmpty()) {
3983 if (mouseEvent->buttons())
3984 return;
3985 QGraphicsSceneHoverEvent hover;
3986 _q_hoverFromMouseEvent(&hover, mouseEvent);
3987 mouseEvent->setAccepted(d->dispatchHoverEvent(&hover));
3988 return;
3989 }
3990
3991 // Forward the event to the mouse grabber
3992 d->sendMouseEvent(mouseEvent);
3993 mouseEvent->accept();
3994}
3995
3996/*!
3997 This event handler, for event \a mouseEvent, can be reimplemented
3998 in a subclass to receive mouse release events for the scene.
3999
4000 The default implementation depends on the mouse grabber state. If
4001 there is no mouse grabber, the event is ignored. Otherwise, if
4002 there is a mouse grabber item, the event is sent to the mouse
4003 grabber. If this mouse release represents the last pressed button
4004 on the mouse, the mouse grabber item then loses the mouse grab.
4005
4006 \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
4007 QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
4008*/
4009void QGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
4010{
4011 Q_D(QGraphicsScene);
4012 if (d->mouseGrabberItems.isEmpty()) {
4013 mouseEvent->ignore();
4014 return;
4015 }
4016
4017 // Forward the event to the mouse grabber
4018 d->sendMouseEvent(mouseEvent);
4019 mouseEvent->accept();
4020
4021 // Reset the mouse grabber when the last mouse button has been released.
4022 if (!mouseEvent->buttons()) {
4023 if (!d->mouseGrabberItems.isEmpty()) {
4024 d->lastMouseGrabberItem = d->mouseGrabberItems.constLast();
4025 if (d->lastMouseGrabberItemHasImplicitMouseGrab)
4026 d->mouseGrabberItems.constLast()->ungrabMouse();
4027 } else {
4028 d->lastMouseGrabberItem = nullptr;
4029 }
4030
4031 // Generate a hoverevent
4032 QGraphicsSceneHoverEvent hoverEvent;
4033 _q_hoverFromMouseEvent(&hoverEvent, mouseEvent);
4034 d->dispatchHoverEvent(&hoverEvent);
4035 }
4036}
4037
4038/*!
4039 This event handler, for event \a mouseEvent, can be reimplemented
4040 in a subclass to receive mouse double-click events for the scene.
4041
4042 If someone doubleclicks on the scene, the scene will first receive
4043 a mouse press event, followed by a release event (i.e., a click),
4044 then a double-click event, and finally a release event. If the
4045 double-click event is delivered to a different item than the one
4046 that received the first press and release, it will be delivered as
4047 a press event. However, tripleclick events are not delivered as
4048 double-click events in this case.
4049
4050 The default implementation is similar to mousePressEvent().
4051
4052 Note: See items() for a definition of which items are considered visible by this function.
4053
4054 \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
4055 QGraphicsItem::mouseReleaseEvent(), QGraphicsItem::setAcceptedMouseButtons()
4056*/
4057void QGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
4058{
4059 Q_D(QGraphicsScene);
4060 d->mousePressEventHandler(mouseEvent);
4061}
4062
4063/*!
4064 This event handler, for event \a wheelEvent, can be reimplemented in a
4065 subclass to receive mouse wheel events for the scene.
4066
4067 By default, the event is delivered to the topmost visible item under the
4068 cursor. If ignored, the event propagates to the item beneath, and again
4069 until the event is accepted, or it reaches the scene. If no items accept
4070 the event, it is ignored.
4071
4072 Note: See items() for a definition of which items are considered visible by this function.
4073
4074 \sa QGraphicsItem::wheelEvent()
4075*/
4076void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent)
4077{
4078 Q_D(QGraphicsScene);
4079 const QList<QGraphicsItem *> wheelCandidates = d->itemsAtPosition(wheelEvent->screenPos(),
4080 wheelEvent->scenePos(),
4081 wheelEvent->widget());
4082
4083 // Find the first popup under the mouse (including the popup's descendants) starting from the last.
4084 // Remove all popups after the one found, or all or them if no popup is under the mouse.
4085 // Then continue with the event.
4086 QList<QGraphicsWidget *>::const_iterator iter = d->popupWidgets.constEnd();
4087 while (iter > d->popupWidgets.constBegin() && !wheelCandidates.isEmpty()) {
4088 --iter;
4089 if (wheelCandidates.first() == *iter || (*iter)->isAncestorOf(wheelCandidates.first()))
4090 break;
4091 d->removePopup(*iter);
4092 }
4093
4094 bool hasSetFocus = false;
4095 for (QGraphicsItem *item : wheelCandidates) {
4096 if (!hasSetFocus && item->isEnabled()
4097 && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
4098 if (item->isWidget() && static_cast<QGraphicsWidget *>(item)->focusPolicy() == Qt::WheelFocus) {
4099 hasSetFocus = true;
4100 if (item != focusItem())
4101 setFocusItem(item, Qt::MouseFocusReason);
4102 }
4103 }
4104
4105 wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(),
4106 wheelEvent->widget()));
4107 wheelEvent->accept();
4108 bool isPanel = item->isPanel();
4109 bool ret = d->sendEvent(item, wheelEvent);
4110
4111 if (ret && (isPanel || wheelEvent->isAccepted()))
4112 break;
4113 }
4114}
4115
4116/*!
4117 This event handler, for event \a event, can be reimplemented in a
4118 subclass to receive input method events for the scene.
4119
4120 The default implementation forwards the event to the focusItem().
4121 If no item currently has focus or the current focus item does not
4122 accept input methods, this function does nothing.
4123
4124 \sa QGraphicsItem::inputMethodEvent()
4125*/
4126void QGraphicsScene::inputMethodEvent(QInputMethodEvent *event)
4127{
4128 Q_D(QGraphicsScene);
4129 if (d->focusItem && (d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) {
4130 d->sendEvent(d->focusItem, event);
4131 return;
4132 }
4133 if (d->lastFocusItem && d->lastFocusItem != d->focusItem && (d->lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod))
4134 d->sendEvent(d->lastFocusItem, event);
4135}
4136
4137/*!
4138 Draws the background of the scene using \a painter, before any items and
4139 the foreground are drawn. Reimplement this function to provide a custom
4140 background for the scene.
4141
4142 All painting is done in \e scene coordinates. The \a rect
4143 parameter is the exposed rectangle.
4144
4145 If all you want is to define a color, texture, or gradient for the
4146 background, you can call setBackgroundBrush() instead.
4147
4148 \sa drawForeground(), drawItems()
4149*/
4150void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
4151{
4152 Q_D(QGraphicsScene);
4153
4154 if (d->backgroundBrush.style() != Qt::NoBrush) {
4155 if (d->painterStateProtection)
4156 painter->save();
4157 painter->setBrushOrigin(0, 0);
4158 painter->fillRect(rect, backgroundBrush());
4159 if (d->painterStateProtection)
4160 painter->restore();
4161 }
4162}
4163
4164/*!
4165 Draws the foreground of the scene using \a painter, after the background
4166 and all items have been drawn. Reimplement this function to provide a
4167 custom foreground for the scene.
4168
4169 All painting is done in \e scene coordinates. The \a rect
4170 parameter is the exposed rectangle.
4171
4172 If all you want is to define a color, texture or gradient for the
4173 foreground, you can call setForegroundBrush() instead.
4174
4175 \sa drawBackground(), drawItems()
4176*/
4177void QGraphicsScene::drawForeground(QPainter *painter, const QRectF &rect)
4178{
4179 Q_D(QGraphicsScene);
4180
4181 if (d->foregroundBrush.style() != Qt::NoBrush) {
4182 if (d->painterStateProtection)
4183 painter->save();
4184 painter->setBrushOrigin(0, 0);
4185 painter->fillRect(rect, foregroundBrush());
4186 if (d->painterStateProtection)
4187 painter->restore();
4188 }
4189}
4190
4191static void _q_paintItem(QGraphicsItem *item, QPainter *painter,
4192 const QStyleOptionGraphicsItem *option, QWidget *widget,
4193 bool useWindowOpacity, bool painterStateProtection)
4194{
4195 if (!item->isWidget()) {
4196 item->paint(painter, option, widget);
4197 return;
4198 }
4199 QGraphicsWidget *widgetItem = static_cast<QGraphicsWidget *>(item);
4200 QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(widgetItem);
4201 const qreal windowOpacity = (proxy && proxy->widget() && useWindowOpacity)
4202 ? proxy->widget()->windowOpacity() : 1.0;
4203 const qreal oldPainterOpacity = painter->opacity();
4204
4205 if (qFuzzyIsNull(windowOpacity))
4206 return;
4207 // Set new painter opacity.
4208 if (windowOpacity < 1.0)
4209 painter->setOpacity(oldPainterOpacity * windowOpacity);
4210
4211 // set layoutdirection on the painter
4212 Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
4213 painter->setLayoutDirection(widgetItem->layoutDirection());
4214
4215 if (widgetItem->isWindow() && widgetItem->windowType() != Qt::Popup && widgetItem->windowType() != Qt::ToolTip
4216 && !(widgetItem->windowFlags() & Qt::FramelessWindowHint)) {
4217 if (painterStateProtection)
4218 painter->save();
4219 widgetItem->paintWindowFrame(painter, option, widget);
4220 if (painterStateProtection)
4221 painter->restore();
4222 } else if (widgetItem->autoFillBackground()) {
4223 painter->fillRect(option->exposedRect, widgetItem->palette().window());
4224 }
4225
4226 widgetItem->paint(painter, option, widget);
4227
4228 // Restore layoutdirection on the painter.
4229 painter->setLayoutDirection(oldLayoutDirection);
4230 // Restore painter opacity.
4231 if (windowOpacity < 1.0)
4232 painter->setOpacity(oldPainterOpacity);
4233}
4234
4235static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &pixmapExposed,
4236 const QTransform &itemToPixmap, QPainter::RenderHints renderHints,
4237 const QStyleOptionGraphicsItem *option, bool painterStateProtection)
4238{
4239 QPixmap subPix;
4240 QPainter pixmapPainter;
4241 QRect br = pixmapExposed.boundingRect();
4242
4243 // Don't use subpixmap if we get a full update.
4244 if (pixmapExposed.isEmpty() || (pixmapExposed.rectCount() == 1 && br.contains(pix->rect()))) {
4245 pix->fill(Qt::transparent);
4246 pixmapPainter.begin(pix);
4247 } else {
4248 subPix = QPixmap(br.size() * pix->devicePixelRatio());
4249 subPix.setDevicePixelRatio(pix->devicePixelRatio());
4250 subPix.fill(Qt::transparent);
4251 pixmapPainter.begin(&subPix);
4252 pixmapPainter.translate(-br.topLeft());
4253 if (!pixmapExposed.isEmpty()) {
4254 // Applied to subPix; paint is adjusted to the coordinate space is
4255 // correct.
4256 pixmapPainter.setClipRegion(pixmapExposed);
4257 }
4258 }
4259
4260 pixmapPainter.setRenderHints(pixmapPainter.renderHints(), false);
4261 pixmapPainter.setRenderHints(renderHints, true);
4262 pixmapPainter.setWorldTransform(itemToPixmap, true);
4263
4264 // Render.
4265 _q_paintItem(item, &pixmapPainter, option, nullptr, false, painterStateProtection);
4266 pixmapPainter.end();
4267
4268 if (!subPix.isNull()) {
4269 // Blit the subpixmap into the main pixmap.
4270 pixmapPainter.begin(pix);
4271 pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
4272 pixmapPainter.setClipRegion(pixmapExposed);
4273 pixmapPainter.drawPixmap(br.topLeft(), subPix);
4274 pixmapPainter.end();
4275 }
4276}
4277
4278// Copied from qpaintengine_vg.cpp
4279// Returns \c true for 90, 180, and 270 degree rotations.
4280static inline bool transformIsSimple(const QTransform& transform)
4281{
4282 QTransform::TransformationType type = transform.type();
4283 if (type <= QTransform::TxScale) {
4284 return true;
4285 } else if (type == QTransform::TxRotate) {
4286 // Check for 90, and 270 degree rotations.
4287 qreal m11 = transform.m11();
4288 qreal m12 = transform.m12();
4289 qreal m21 = transform.m21();
4290 qreal m22 = transform.m22();
4291 if (m11 == 0.0f && m22 == 0.0f) {
4292 if (m12 == 1.0f && m21 == -1.0f)
4293 return true; // 90 degrees.
4294 else if (m12 == -1.0f && m21 == 1.0f)
4295 return true; // 270 degrees.
4296 else if (m12 == -1.0f && m21 == -1.0f)
4297 return true; // 90 degrees inverted y.
4298 else if (m12 == 1.0f && m21 == 1.0f)
4299 return true; // 270 degrees inverted y.
4300 }
4301 }
4302 return false;
4303}
4304
4305/*!
4306 \internal
4307
4308 Draws items directly, or using cache.
4309*/
4310void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painter,
4311 const QStyleOptionGraphicsItem *option, QWidget *widget,
4312 bool painterStateProtection)
4313{
4314 QGraphicsItemPrivate *itemd = item->d_ptr.data();
4315 QGraphicsItem::CacheMode cacheMode = QGraphicsItem::CacheMode(itemd->cacheMode);
4316
4317 // Render directly, using no cache.
4318 if (cacheMode == QGraphicsItem::NoCache) {
4319 _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget, true, painterStateProtection);
4320 return;
4321 }
4322
4323 const qreal devicePixelRatio = painter->device()->devicePixelRatio();
4324 const qreal oldPainterOpacity = painter->opacity();
4325 qreal newPainterOpacity = oldPainterOpacity;
4326 QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0;
4327 if (proxy && proxy->widget()) {
4328 const qreal windowOpacity = proxy->widget()->windowOpacity();
4329 if (windowOpacity < 1.0)
4330 newPainterOpacity *= windowOpacity;
4331 }
4332
4333 // Item's (local) bounding rect
4334 QRectF brect = item->boundingRect();
4335 QRectF adjustedBrect(brect);
4336 _q_adjustRect(&adjustedBrect);
4337 if (adjustedBrect.isEmpty())
4338 return;
4339
4340 // Fetch the off-screen transparent buffer and exposed area info.
4341 QPixmapCache::Key pixmapKey;
4342 QPixmap pix;
4343
4344 bool pixmapFound;
4345 QGraphicsItemCache *itemCache = itemd->extraItemCache();
4346 if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
4347 pixmapKey = itemCache->key;
4348 } else {
4349 pixmapKey = itemCache->deviceData.value(widget).key;
4350 }
4351
4352 // Find pixmap in cache.
4353 pixmapFound = QPixmapCache::find(pixmapKey, &pix);
4354
4355 // Render using item coordinate cache mode.
4356 if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
4357 QSize pixmapSize;
4358 bool fixedCacheSize = itemCache->fixedSize.isValid();
4359 QRect br = brect.toAlignedRect();
4360 if (fixedCacheSize) {
4361 pixmapSize = itemCache->fixedSize;
4362 } else {
4363 pixmapSize = br.size();
4364 }
4365
4366 pixmapSize *= devicePixelRatio;
4367
4368 // Create or recreate the pixmap.
4369 int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
4370 QSize adjustSize(adjust*2, adjust*2);
4371 br.adjust(-adjust / devicePixelRatio, -adjust / devicePixelRatio, adjust / devicePixelRatio, adjust / devicePixelRatio);
4372 if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
4373 pix = QPixmap(pixmapSize + adjustSize);
4374 itemCache->boundingRect = br;
4375 itemCache->exposed.clear();
4376 itemCache->allExposed = true;
4377 } else if (itemCache->boundingRect != br) {
4378 itemCache->boundingRect = br;
4379 itemCache->exposed.clear();
4380 itemCache->allExposed = true;
4381 }
4382
4383 // Redraw any newly exposed areas.
4384 if (itemCache->allExposed || !itemCache->exposed.isEmpty()) {
4385
4386 //We know that we will modify the pixmap, removing it from the cache
4387 //will detach the one we have and avoid a deep copy
4388 if (pixmapFound)
4389 QPixmapCache::remove(pixmapKey);
4390
4391 // Fit the item's bounding rect into the pixmap's coordinates.
4392 QTransform itemToPixmap;
4393 if (fixedCacheSize) {
4394 const QPointF scale((pixmapSize.width() / devicePixelRatio) / brect.width(),
4395 (pixmapSize.height() / devicePixelRatio) / brect.height());
4396 itemToPixmap.scale(scale.x(), scale.y());
4397 }
4398 itemToPixmap.translate(-br.x(), -br.y());
4399
4400 // Generate the item's exposedRect and map its list of expose
4401 // rects to device coordinates.
4402 styleOptionTmp = *option;
4403 QRegion pixmapExposed;
4404 QRectF exposedRect;
4405 if (!itemCache->allExposed) {
4406 for (const auto &rect : std::as_const(itemCache->exposed)) {
4407 exposedRect |= rect;
4408 pixmapExposed += itemToPixmap.mapRect(rect).toAlignedRect();
4409 }
4410 } else {
4411 exposedRect = brect;
4412 }
4413 styleOptionTmp.exposedRect = exposedRect;
4414
4415 // Render.
4416 pix.setDevicePixelRatio(devicePixelRatio);
4417 _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
4418 &styleOptionTmp, painterStateProtection);
4419
4420 // insert this pixmap into the cache.
4421 itemCache->key = QPixmapCache::insert(pix);
4422
4423 // Reset expose data.
4424 itemCache->allExposed = false;
4425 itemCache->exposed.clear();
4426 }
4427
4428 // Redraw the exposed area using the transformed painter. Depending on
4429 // the hardware, this may be a server-side operation, or an expensive
4430 // qpixmap-image-transform-pixmap roundtrip.
4431 if (newPainterOpacity != oldPainterOpacity) {
4432 painter->setOpacity(newPainterOpacity);
4433 painter->drawPixmap(br.topLeft(), pix);
4434 painter->setOpacity(oldPainterOpacity);
4435 } else {
4436 painter->drawPixmap(br.topLeft(), pix);
4437 }
4438 return;
4439 }
4440
4441 // Render using device coordinate cache mode.
4442 if (cacheMode == QGraphicsItem::DeviceCoordinateCache) {
4443 // Find the item's bounds in device coordinates.
4444 QRectF deviceBounds = painter->worldTransform().mapRect(brect);
4445 QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
4446 if (deviceRect.isEmpty())
4447 return;
4448 QRect viewRect = widget ? widget->rect() : QRect();
4449 if (widget && !viewRect.intersects(deviceRect))
4450 return;
4451
4452 // Resort to direct rendering if the device rect exceeds the
4453 // (optional) maximum bounds. (QGraphicsSvgItem uses this).
4454 QSize maximumCacheSize =
4455 itemd->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize();
4456 if (!maximumCacheSize.isEmpty()
4457 && (deviceRect.width() > maximumCacheSize.width()
4458 || deviceRect.height() > maximumCacheSize.height())) {
4459 _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget,
4460 oldPainterOpacity != newPainterOpacity, painterStateProtection);
4461 return;
4462 }
4463
4464 // Create or reuse offscreen pixmap, possibly scroll/blit from the old one.
4465 // If the world transform is rotated we always recreate the cache to avoid
4466 // wrong blending.
4467 bool pixModified = false;
4468 QGraphicsItemCache::DeviceData *deviceData = &itemCache->deviceData[widget];
4469 bool invertable = true;
4470 QTransform diff = deviceData->lastTransform.inverted(&invertable);
4471 if (invertable)
4472 diff *= painter->worldTransform();
4473 deviceData->lastTransform = painter->worldTransform();
4474 bool allowPartialCacheExposure = false;
4475 bool simpleTransform = invertable && diff.type() <= QTransform::TxTranslate
4476 && transformIsSimple(painter->worldTransform());
4477 if (!simpleTransform) {
4478 pixModified = true;
4479 itemCache->allExposed = true;
4480 itemCache->exposed.clear();
4481 deviceData->cacheIndent = QPoint();
4482 pix = QPixmap();
4483 } else if (!viewRect.isNull()) {
4484 allowPartialCacheExposure = deviceData->cacheIndent != QPoint();
4485 }
4486
4487 // Allow partial cache exposure if the device rect isn't fully contained and
4488 // deviceRect is 20% taller or wider than the viewRect.
4489 if (!allowPartialCacheExposure && !viewRect.isNull() && !viewRect.contains(deviceRect)) {
4490 allowPartialCacheExposure = (viewRect.width() * 1.2 < deviceRect.width())
4491 || (viewRect.height() * 1.2 < deviceRect.height());
4492 }
4493
4494 QRegion scrollExposure;
4495 if (allowPartialCacheExposure) {
4496 // Part of pixmap is drawn. Either device contains viewrect (big
4497 // item covers whole screen) or parts of device are outside the
4498 // viewport. In either case the device rect must be the intersect
4499 // between the two.
4500 int dx = deviceRect.left() < viewRect.left() ? viewRect.left() - deviceRect.left() : 0;
4501 int dy = deviceRect.top() < viewRect.top() ? viewRect.top() - deviceRect.top() : 0;
4502 QPoint newCacheIndent(dx, dy);
4503 deviceRect &= viewRect;
4504
4505 if (pix.isNull()) {
4506 deviceData->cacheIndent = QPoint();
4507 itemCache->allExposed = true;
4508 itemCache->exposed.clear();
4509 pixModified = true;
4510 }
4511
4512 // Copy / "scroll" the old pixmap onto the new ole and calculate
4513 // scrolled exposure.
4514 if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size() / devicePixelRatio) {
4515 QPoint diff = newCacheIndent - deviceData->cacheIndent;
4516 QPixmap newPix(deviceRect.size() * devicePixelRatio);
4517 // ### Investigate removing this fill (test with Plasma and
4518 // graphicssystem raster).
4519 newPix.fill(Qt::transparent);
4520 if (!pix.isNull()) {
4521 newPix.setDevicePixelRatio(devicePixelRatio);
4522 QPainter newPixPainter(&newPix);
4523 newPixPainter.drawPixmap(-diff, pix);
4524 newPixPainter.end();
4525 }
4526 QRegion exposed;
4527 exposed += QRect(QPoint(0,0), newPix.size() / devicePixelRatio);
4528 if (!pix.isNull())
4529 exposed -= QRect(-diff, pix.size() / devicePixelRatio);
4530 scrollExposure = exposed;
4531
4532 pix = newPix;
4533 pixModified = true;
4534 }
4535 deviceData->cacheIndent = newCacheIndent;
4536 } else {
4537 // Full pixmap is drawn.
4538 deviceData->cacheIndent = QPoint();
4539
4540 // Auto-adjust the pixmap size.
4541 if (deviceRect.size() != pix.size() / devicePixelRatio) {
4542 // exposed needs to cover the whole pixmap
4543 pix = QPixmap(deviceRect.size() * devicePixelRatio);
4544 pixModified = true;
4545 itemCache->allExposed = true;
4546 itemCache->exposed.clear();
4547 }
4548 }
4549
4550 // Check for newly invalidated areas.
4551 if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) {
4552 //We know that we will modify the pixmap, removing it from the cache
4553 //will detach the one we have and avoid a deep copy
4554 if (pixmapFound)
4555 QPixmapCache::remove(pixmapKey);
4556
4557 // Construct an item-to-pixmap transform.
4558 QPointF p = deviceRect.topLeft();
4559 QTransform itemToPixmap = painter->worldTransform();
4560 if (!p.isNull())
4561 itemToPixmap *= QTransform::fromTranslate(-p.x(), -p.y());
4562
4563 // Map the item's logical expose to pixmap coordinates.
4564 QRegion pixmapExposed = scrollExposure;
4565 if (!itemCache->allExposed) {
4566 for (const auto &rect : std::as_const(itemCache->exposed))
4567 pixmapExposed += itemToPixmap.mapRect(rect).toRect().adjusted(-1, -1, 1, 1);
4568 }
4569
4570 // Calculate the style option's exposedRect.
4571 QRectF br;
4572 if (itemCache->allExposed) {
4573 br = item->boundingRect();
4574 } else {
4575 for (const auto &rect : std::as_const(itemCache->exposed))
4576 br |= rect;
4577 QTransform pixmapToItem = itemToPixmap.inverted();
4578 for (const QRect &r : std::as_const(scrollExposure))
4579 br |= pixmapToItem.mapRect(r);
4580 }
4581 styleOptionTmp = *option;
4582 styleOptionTmp.exposedRect = br.adjusted(-1, -1, 1, 1);
4583
4584 // Render the exposed areas.
4585 pix.setDevicePixelRatio(devicePixelRatio);
4586 _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
4587 &styleOptionTmp, painterStateProtection);
4588
4589 // Reset expose data.
4590 pixModified = true;
4591 itemCache->allExposed = false;
4592 itemCache->exposed.clear();
4593 }
4594
4595 if (pixModified) {
4596 // Insert this pixmap into the cache.
4597 deviceData->key = QPixmapCache::insert(pix);
4598 }
4599
4600 // Redraw the exposed area using an untransformed painter. This
4601 // effectively becomes a bitblit that does not transform the cache.
4602 QTransform restoreTransform = painter->worldTransform();
4603 painter->setWorldTransform(QTransform());
4604 if (newPainterOpacity != oldPainterOpacity) {
4605 painter->setOpacity(newPainterOpacity);
4606 painter->drawPixmap(deviceRect.topLeft(), pix);
4607 painter->setOpacity(oldPainterOpacity);
4608 } else {
4609 painter->drawPixmap(deviceRect.topLeft(), pix);
4610 }
4611 painter->setWorldTransform(restoreTransform);
4612 return;
4613 }
4614}
4615
4616void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const viewTransform,
4617 QRegion *exposedRegion, QWidget *widget)
4618{
4619 // Make sure we don't have unpolished items before we draw.
4620 if (!unpolishedItems.isEmpty())
4621 _q_polishItems();
4622
4623 updateAll = false;
4624 QRectF exposedSceneRect;
4625 if (exposedRegion && indexMethod != QGraphicsScene::NoIndex) {
4626 exposedSceneRect = exposedRegion->boundingRect().adjusted(-1, -1, 1, 1);
4627 if (viewTransform)
4628 exposedSceneRect = viewTransform->inverted().mapRect(exposedSceneRect);
4629 }
4630 const QList<QGraphicsItem *> tli = index->estimateTopLevelItems(exposedSceneRect, Qt::AscendingOrder);
4631 for (const auto subTree : tli)
4632 drawSubtreeRecursive(subTree, painter, viewTransform, exposedRegion, widget);
4633}
4634
4635void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter,
4636 const QTransform *const viewTransform,
4637 QRegion *exposedRegion, QWidget *widget,
4638 qreal parentOpacity, const QTransform *const effectTransform)
4639{
4640 Q_ASSERT(item);
4641
4642 if (!item->d_ptr->visible)
4643 return;
4644
4645 const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
4646 const bool itemHasChildren = !item->d_ptr->children.isEmpty();
4647 if (!itemHasContents && !itemHasChildren)
4648 return; // Item has neither contents nor children!(?)
4649
4650 const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
4651 const bool itemIsFullyTransparent = QGraphicsItemPrivate::isOpacityNull(opacity);
4652 if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity()))
4653 return;
4654
4655 QTransform transform(Qt::Uninitialized);
4656 QTransform *transformPtr = nullptr;
4657 bool translateOnlyTransform = false;
4658#define ENSURE_TRANSFORM_PTR
4659 if (!transformPtr) {
4660 Q_ASSERT(!itemIsUntransformable);
4661 if (viewTransform) {
4662 transform = item->d_ptr->sceneTransform;
4663 transform *= *viewTransform;
4664 transformPtr = &transform;
4665 } else {
4666 transformPtr = &item->d_ptr->sceneTransform;
4667 translateOnlyTransform = item->d_ptr->sceneTransformTranslateOnly;
4668 }
4669 }
4670
4671 // Update the item's scene transform if the item is transformable;
4672 // otherwise calculate the full transform,
4673 bool wasDirtyParentSceneTransform = false;
4674 const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
4675 if (itemIsUntransformable) {
4676 transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform());
4677 transformPtr = &transform;
4678 } else if (item->d_ptr->dirtySceneTransform) {
4679 item->d_ptr->updateSceneTransformFromParent();
4680 Q_ASSERT(!item->d_ptr->dirtySceneTransform);
4681 wasDirtyParentSceneTransform = true;
4682 }
4683
4684 const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape
4685 || item->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape);
4686 bool drawItem = itemHasContents && !itemIsFullyTransparent;
4687 if (drawItem || minimumRenderSize > 0.0) {
4688 const QRectF brect = adjustedItemEffectiveBoundingRect(item);
4690 QRectF preciseViewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy())
4691 : transformPtr->mapRect(brect);
4692
4693 bool itemIsTooSmallToRender = false;
4694 if (minimumRenderSize > 0.0
4695 && (preciseViewBoundingRect.width() < minimumRenderSize
4696 || preciseViewBoundingRect.height() < minimumRenderSize)) {
4697 itemIsTooSmallToRender = true;
4698 drawItem = false;
4699 }
4700
4701 bool itemIsOutsideVisibleRect = false;
4702 if (drawItem) {
4703 QRect viewBoundingRect = preciseViewBoundingRect.toAlignedRect();
4704 viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust);
4705 if (widget)
4706 item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
4707 drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect)
4708 : !viewBoundingRect.normalized().isEmpty();
4709 itemIsOutsideVisibleRect = !drawItem;
4710 }
4711
4712 if (itemIsTooSmallToRender || itemIsOutsideVisibleRect) {
4713 // We cannot simply use !drawItem here. If we did it is possible
4714 // to enter the outer if statement with drawItem == false and minimumRenderSize > 0
4715 // and finally end up inside this inner if, even though none of the above two
4716 // conditions are met. In that case we should not return from this function
4717 // but call draw() instead.
4718 if (!itemHasChildren)
4719 return;
4720 if (itemClipsChildrenToShape) {
4721 if (wasDirtyParentSceneTransform)
4722 item->d_ptr->invalidateChildrenSceneTransform();
4723 return;
4724 }
4725 }
4726 } // else we know for sure this item has children we must process.
4727
4728 if (itemHasChildren && itemClipsChildrenToShape)
4730
4731#if QT_CONFIG(graphicseffect)
4732 if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) {
4733 ENSURE_TRANSFORM_PTR;
4734 QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp,
4735 painter, opacity, wasDirtyParentSceneTransform, itemHasContents && !itemIsFullyTransparent);
4736 QGraphicsEffectSource *source = item->d_ptr->graphicsEffect->d_func()->source;
4737 QGraphicsItemEffectSourcePrivate *sourced = static_cast<QGraphicsItemEffectSourcePrivate *>
4738 (source->d_func());
4739 sourced->info = &info;
4740 const QTransform restoreTransform = painter->worldTransform();
4741 if (effectTransform)
4742 painter->setWorldTransform(*transformPtr * *effectTransform);
4743 else
4744 painter->setWorldTransform(*transformPtr);
4745 painter->setOpacity(opacity);
4746
4747 if (sourced->currentCachedSystem() != Qt::LogicalCoordinates
4748 && sourced->lastEffectTransform != painter->worldTransform())
4749 {
4750 if (sourced->lastEffectTransform.type() <= QTransform::TxTranslate
4751 && painter->worldTransform().type() <= QTransform::TxTranslate)
4752 {
4753 QRectF sourceRect = sourced->boundingRect(Qt::DeviceCoordinates);
4754 QRect effectRect = sourced->paddedEffectRect(Qt::DeviceCoordinates, sourced->currentCachedMode(), sourceRect).toAlignedRect();
4755
4756 sourced->setCachedOffset(effectRect.topLeft());
4757 } else {
4758 sourced->invalidateCache(QGraphicsEffectSourcePrivate::TransformChanged);
4759 }
4760
4761 sourced->lastEffectTransform = painter->worldTransform();
4762 }
4763
4764 item->d_ptr->graphicsEffect->draw(painter);
4765 painter->setWorldTransform(restoreTransform);
4766 sourced->info = nullptr;
4767 } else
4768#endif // QT_CONFIG(graphicseffect)
4769 {
4770 draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity,
4771 effectTransform, wasDirtyParentSceneTransform, drawItem);
4772 }
4773}
4774
4775static inline void setClip(QPainter *painter, QGraphicsItem *item)
4776{
4777 painter->save();
4778 QRectF clipRect;
4779 const QPainterPath clipPath(item->shape());
4780 if (QPathClipper::pathToRect(clipPath, &clipRect))
4781 painter->setClipRect(clipRect.normalized(), Qt::IntersectClip);
4782 else
4783 painter->setClipPath(clipPath, Qt::IntersectClip);
4784}
4785
4786static inline void setWorldTransform(QPainter *painter, const QTransform *const transformPtr,
4787 const QTransform *effectTransform)
4788{
4789 Q_ASSERT(transformPtr);
4790 if (effectTransform)
4791 painter->setWorldTransform(*transformPtr * *effectTransform);
4792 else
4793 painter->setWorldTransform(*transformPtr);
4794}
4795
4796void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform,
4797 const QTransform *const transformPtr, QRegion *exposedRegion, QWidget *widget,
4798 qreal opacity, const QTransform *effectTransform,
4799 bool wasDirtyParentSceneTransform, bool drawItem)
4800{
4801 const auto &children = item->d_ptr->children;
4802
4803 const bool itemIsFullyTransparent = QGraphicsItemPrivate::isOpacityNull(opacity);
4804 const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
4805 const bool itemHasChildren = !children.isEmpty();
4806 bool setChildClip = itemClipsChildrenToShape;
4807 bool itemHasChildrenStackedBehind = false;
4808
4809 int i = 0;
4810 if (itemHasChildren) {
4811 if (itemClipsChildrenToShape)
4812 setWorldTransform(painter, transformPtr, effectTransform);
4813
4814 item->d_ptr->ensureSortedChildren();
4815 // Items with the 'ItemStacksBehindParent' flag are put in front of the list
4816 // so all we have to do is to check the first item.
4817 itemHasChildrenStackedBehind = (children.at(0)->d_ptr->flags
4818 & QGraphicsItem::ItemStacksBehindParent);
4819
4820 if (itemHasChildrenStackedBehind) {
4821 if (itemClipsChildrenToShape) {
4822 setClip(painter, item);
4823 setChildClip = false;
4824 }
4825
4826 // Draw children behind
4827 for (i = 0; i < children.size(); ++i) {
4828 QGraphicsItem *child = children.at(i);
4829 if (wasDirtyParentSceneTransform)
4830 child->d_ptr->dirtySceneTransform = 1;
4831 if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent))
4832 break;
4833 if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
4834 continue;
4835 drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
4836 }
4837 }
4838 }
4839
4840 // Draw item
4841 if (drawItem) {
4842 Q_ASSERT(!itemIsFullyTransparent);
4843 Q_ASSERT(!(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents));
4844 Q_ASSERT(transformPtr);
4845 item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion
4846 ? *exposedRegion : QRegion(), exposedRegion == nullptr);
4847
4848 const bool itemClipsToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsToShape;
4849 bool restorePainterClip = false;
4850
4851 if (!itemHasChildren || !itemClipsChildrenToShape) {
4852 // Item does not have children or clip children to shape.
4853 setWorldTransform(painter, transformPtr, effectTransform);
4854 if ((restorePainterClip = itemClipsToShape))
4855 setClip(painter, item);
4856 } else if (itemHasChildrenStackedBehind){
4857 // Item clips children to shape and has children stacked behind, which means
4858 // the painter is already clipped to the item's shape.
4859 if (itemClipsToShape) {
4860 // The clip is already correct. Ensure correct world transform.
4861 setWorldTransform(painter, transformPtr, effectTransform);
4862 } else {
4863 // Remove clip (this also ensures correct world transform).
4864 painter->restore();
4865 setChildClip = true;
4866 }
4867 } else if (itemClipsToShape) {
4868 // Item clips children and itself to shape. It does not have hildren stacked
4869 // behind, which means the clip has not yet been set. We set it now and re-use it
4870 // for the children.
4871 setClip(painter, item);
4872 setChildClip = false;
4873 }
4874
4875 if (painterStateProtection && !restorePainterClip)
4876 painter->save();
4877
4878 painter->setOpacity(opacity);
4879 if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget)
4880 item->paint(painter, &styleOptionTmp, widget);
4881 else
4882 drawItemHelper(item, painter, &styleOptionTmp, widget, painterStateProtection);
4883
4884 if (painterStateProtection || restorePainterClip)
4885 painter->restore();
4886
4887 static int drawRect = qEnvironmentVariableIntValue("QT_DRAW_SCENE_ITEM_RECTS");
4888 if (drawRect) {
4889 QPen oldPen = painter->pen();
4890 QBrush oldBrush = painter->brush();
4891 quintptr ptr = reinterpret_cast<quintptr>(item);
4892 const QColor color = QColor::fromHsv(ptr % 255, 255, 255);
4893 painter->setPen(color);
4894 painter->setBrush(Qt::NoBrush);
4895 painter->drawRect(adjustedItemBoundingRect(item));
4896 painter->setPen(oldPen);
4897 painter->setBrush(oldBrush);
4898 }
4899 }
4900
4901 // Draw children in front
4902 if (itemHasChildren) {
4903 if (setChildClip)
4904 setClip(painter, item);
4905
4906 for (; i < children.size(); ++i) {
4907 QGraphicsItem *child = children.at(i);
4908 if (wasDirtyParentSceneTransform)
4909 child->d_ptr->dirtySceneTransform = 1;
4910 if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
4911 continue;
4912 drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
4913 }
4914
4915 // Restore child clip
4916 if (itemClipsChildrenToShape)
4917 painter->restore();
4918 }
4919}
4920
4921void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, bool invalidateChildren,
4922 bool force, bool ignoreOpacity, bool removingItemFromScene,
4923 bool updateBoundingRect)
4924{
4925 Q_ASSERT(item);
4926 if (updateAll)
4927 return;
4928
4929 if (removingItemFromScene && !ignoreOpacity && !item->d_ptr->ignoreOpacity) {
4930 // If any of the item's ancestors ignore opacity, it means that the opacity
4931 // was set to 0 (and the update request has not yet been processed). That
4932 // also means that we have to ignore the opacity for the item itself; otherwise
4933 // things like: parent->setOpacity(0); scene->removeItem(child) won't work.
4934 // Note that we only do this when removing items from the scene. In all other
4935 // cases the ignoreOpacity bit propagates properly in processDirtyItems, but
4936 // since the item is removed immediately it won't be processed there.
4937 QGraphicsItem *p = item->d_ptr->parent;
4938 while (p) {
4939 if (p->d_ptr->ignoreOpacity) {
4940 item->d_ptr->ignoreOpacity = true;
4941 break;
4942 }
4943 p = p->d_ptr->parent;
4944 }
4945 }
4946
4947 if (item->d_ptr->discardUpdateRequest(/*ignoreVisibleBit=*/force,
4948 /*ignoreDirtyBit=*/removingItemFromScene || invalidateChildren,
4949 /*ignoreOpacity=*/ignoreOpacity)) {
4950 if (item->d_ptr->dirty) {
4951 // The item is already marked as dirty and will be processed later. However,
4952 // we have to make sure ignoreVisible and ignoreOpacity are set properly;
4953 // otherwise things like: item->update(); item->hide() (force is now true)
4954 // won't work as expected.
4955 if (force)
4956 item->d_ptr->ignoreVisible = 1;
4957 if (ignoreOpacity)
4958 item->d_ptr->ignoreOpacity = 1;
4959 }
4960 return;
4961 }
4962
4963 const bool fullItemUpdate = rect.isNull();
4964 if (!fullItemUpdate && rect.isEmpty())
4965 return;
4966
4967 if (!processDirtyItemsEmitted) {
4968 QMetaMethod method = q_ptr->metaObject()->method(processDirtyItemsIndex);
4969 method.invoke(q_ptr, Qt::QueuedConnection);
4970// QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection);
4971 processDirtyItemsEmitted = true;
4972 }
4973
4974 if (removingItemFromScene) {
4975 // Note that this function can be called from the item's destructor, so
4976 // do NOT call any virtual functions on it within this block.
4977 if (isSignalConnected(changedSignalIndex) || views.isEmpty()) {
4978 // This block of code is kept for compatibility. Since 4.5, by default
4979 // QGraphicsView does not connect the signal and we use the below
4980 // method of delivering updates.
4981 q_func()->update();
4982 return;
4983 }
4984
4985 for (auto view : std::as_const(views)) {
4986 QGraphicsViewPrivate *viewPrivate = view->d_func();
4987 QRect rect = item->d_ptr->paintedViewBoundingRects.value(viewPrivate->viewport);
4988 rect.translate(viewPrivate->dirtyScrollOffset);
4989 viewPrivate->updateRect(rect);
4990 }
4991 return;
4992 }
4993
4994 bool hasNoContents = item->d_ptr->flags & QGraphicsItem::ItemHasNoContents;
4995 if (!hasNoContents) {
4996 item->d_ptr->dirty = 1;
4997 if (fullItemUpdate)
4998 item->d_ptr->fullUpdatePending = 1;
4999 else if (!item->d_ptr->fullUpdatePending)
5000 item->d_ptr->needsRepaint |= rect;
5001 } else if (item->d_ptr->graphicsEffect) {
5002 invalidateChildren = true;
5003 }
5004
5005 if (invalidateChildren) {
5006 item->d_ptr->allChildrenDirty = 1;
5007 item->d_ptr->dirtyChildren = 1;
5008 }
5009
5010 if (force)
5011 item->d_ptr->ignoreVisible = 1;
5012 if (ignoreOpacity)
5013 item->d_ptr->ignoreOpacity = 1;
5014
5015 if (!updateBoundingRect)
5016 item->d_ptr->markParentDirty();
5017}
5018
5019static inline bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item,
5020 const QRectF &rect, bool itemIsUntransformable)
5021{
5022 Q_ASSERT(view);
5023 Q_ASSERT(item);
5024
5025 QGraphicsItem *itemq = static_cast<QGraphicsItem *>(item->q_ptr);
5026 QGraphicsView *viewq = static_cast<QGraphicsView *>(view->q_ptr);
5027
5028 if (itemIsUntransformable) {
5029 const QTransform xform = itemq->deviceTransform(viewq->viewportTransform());
5030 if (!item->hasBoundingRegionGranularity)
5031 return view->updateRectF(xform.mapRect(rect));
5032 return view->updateRegion(rect, xform);
5033 }
5034
5035 if (item->sceneTransformTranslateOnly && view->identityMatrix) {
5036 const qreal dx = item->sceneTransform.dx();
5037 const qreal dy = item->sceneTransform.dy();
5038 QRectF r(rect);
5039 r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll());
5040 return view->updateRectF(r);
5041 }
5042
5043 if (!viewq->isTransformed()) {
5044 if (!item->hasBoundingRegionGranularity)
5045 return view->updateRectF(item->sceneTransform.mapRect(rect));
5046 return view->updateRegion(rect, item->sceneTransform);
5047 }
5048
5049 QTransform xform = item->sceneTransform;
5050 xform *= viewq->viewportTransform();
5051 if (!item->hasBoundingRegionGranularity)
5052 return view->updateRectF(xform.mapRect(rect));
5053 return view->updateRegion(rect, xform);
5054}
5055
5056void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren,
5057 qreal parentOpacity)
5058{
5059 Q_Q(QGraphicsScene);
5060 Q_ASSERT(item);
5061 Q_ASSERT(!updateAll);
5062
5063 if (!item->d_ptr->dirty && !item->d_ptr->dirtyChildren) {
5064 resetDirtyItem(item);
5065 return;
5066 }
5067
5068 const bool itemIsHidden = !item->d_ptr->ignoreVisible && !item->d_ptr->visible;
5069 if (itemIsHidden) {
5070 resetDirtyItem(item, /*recursive=*/true);
5071 return;
5072 }
5073
5074 bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
5075 const bool itemHasChildren = !item->d_ptr->children.isEmpty();
5076 if (!itemHasContents) {
5077 if (!itemHasChildren) {
5078 resetDirtyItem(item);
5079 return; // Item has neither contents nor children!(?)
5080 }
5081 if (item->d_ptr->graphicsEffect)
5082 itemHasContents = true;
5083 }
5084
5085 const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
5086 const bool itemIsFullyTransparent = !item->d_ptr->ignoreOpacity
5087 && QGraphicsItemPrivate::isOpacityNull(opacity);
5088 if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) {
5089 resetDirtyItem(item, /*recursive=*/itemHasChildren);
5090 return;
5091 }
5092
5093 bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform;
5094 const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
5095 if (wasDirtyParentSceneTransform && !itemIsUntransformable) {
5096 item->d_ptr->updateSceneTransformFromParent();
5097 Q_ASSERT(!item->d_ptr->dirtySceneTransform);
5098 }
5099
5100 const bool wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint;
5101 if (itemIsFullyTransparent || !itemHasContents || dirtyAncestorContainsChildren) {
5102 // Make sure we don't process invisible items or items with no content.
5103 item->d_ptr->dirty = 0;
5104 item->d_ptr->fullUpdatePending = 0;
5105 // Might have a dirty view bounding rect otherwise.
5106 if (itemIsFullyTransparent || !itemHasContents)
5107 item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0;
5108 }
5109
5110 if (!hasSceneRect && item->d_ptr->geometryChanged && item->d_ptr->visible) {
5111 // Update growingItemsBoundingRect.
5112 if (item->d_ptr->sceneTransformTranslateOnly) {
5113 growingItemsBoundingRect |= item->boundingRect().translated(item->d_ptr->sceneTransform.dx(),
5114 item->d_ptr->sceneTransform.dy());
5115 } else {
5116 growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect());
5117 }
5118 }
5119
5120 // Process item.
5121 if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
5122 const bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex);
5123 const QRectF itemBoundingRect = adjustedItemEffectiveBoundingRect(item);
5124
5125 if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) {
5126 // This block of code is kept for compatibility. Since 4.5, by default
5127 // QGraphicsView does not connect the signal and we use the below
5128 // method of delivering updates.
5129 if (item->d_ptr->sceneTransformTranslateOnly) {
5130 q->update(itemBoundingRect.translated(item->d_ptr->sceneTransform.dx(),
5131 item->d_ptr->sceneTransform.dy()));
5132 } else {
5133 QRectF rect = item->d_ptr->sceneTransform.mapRect(itemBoundingRect);
5134 if (!rect.isEmpty())
5135 q->update(rect);
5136 }
5137 } else {
5138 QRectF dirtyRect;
5139 bool uninitializedDirtyRect = true;
5140
5141 for (auto view : std::as_const(views)) {
5142 QGraphicsViewPrivate *viewPrivate = view->d_func();
5143 QRect &paintedViewBoundingRect = item->d_ptr->paintedViewBoundingRects[viewPrivate->viewport];
5144 if (viewPrivate->fullUpdatePending
5145 || viewPrivate->viewportUpdateMode == QGraphicsView::NoViewportUpdate) {
5146 // Okay, if we have a full update pending or no viewport update, this item's
5147 // paintedViewBoundingRect will be updated correctly in the next paintEvent if
5148 // it is inside the viewport, but for now we can pretend that it is outside.
5149 paintedViewBoundingRect = QRect(-1, -1, -1, -1);
5150 continue;
5151 }
5152
5153 if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
5154 paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset);
5155 if (!viewPrivate->updateRect(paintedViewBoundingRect))
5156 paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
5157 }
5158
5159 if (!item->d_ptr->dirty)
5160 continue;
5161
5162 if (!item->d_ptr->paintedViewBoundingRectsNeedRepaint
5163 && paintedViewBoundingRect.x() == -1 && paintedViewBoundingRect.y() == -1
5164 && paintedViewBoundingRect.width() == -1 && paintedViewBoundingRect.height() == -1) {
5165 continue; // Outside viewport.
5166 }
5167
5168 if (uninitializedDirtyRect) {
5169 dirtyRect = itemBoundingRect;
5170 if (!item->d_ptr->fullUpdatePending) {
5171 _q_adjustRect(&item->d_ptr->needsRepaint);
5172 dirtyRect &= item->d_ptr->needsRepaint;
5173 }
5174 uninitializedDirtyRect = false;
5175 }
5176
5177 if (dirtyRect.isEmpty())
5178 continue; // Discard updates outside the bounding rect.
5179
5180 if (!updateHelper(viewPrivate, item->d_ptr.data(), dirtyRect, itemIsUntransformable)
5181 && item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
5182 paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
5183 }
5184 }
5185 }
5186 }
5187
5188 // Process children.
5189 if (itemHasChildren && item->d_ptr->dirtyChildren) {
5190 const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape
5191 || item->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape;
5192 // Items with no content are threated as 'dummy' items which means they are never drawn and
5193 // 'processed', so the painted view bounding rect is never up-to-date. This means that whenever
5194 // such an item changes geometry, its children have to take care of the update regardless
5195 // of whether the item clips children to shape or not.
5196 const bool bypassUpdateClip = !itemHasContents && wasDirtyParentViewBoundingRects;
5197 if (itemClipsChildrenToShape && !bypassUpdateClip) {
5198 // Make sure child updates are clipped to the item's bounding rect.
5199 for (auto view : std::as_const(views))
5200 view->d_func()->setUpdateClip(item);
5201 }
5202 if (!dirtyAncestorContainsChildren) {
5203 dirtyAncestorContainsChildren = item->d_ptr->fullUpdatePending
5204 && itemClipsChildrenToShape;
5205 }
5206 const bool allChildrenDirty = item->d_ptr->allChildrenDirty;
5207 const bool parentIgnoresVisible = item->d_ptr->ignoreVisible;
5208 const bool parentIgnoresOpacity = item->d_ptr->ignoreOpacity;
5209 for (auto child : std::as_const(item->d_ptr->children)) {
5210 if (wasDirtyParentSceneTransform)
5211 child->d_ptr->dirtySceneTransform = 1;
5212 if (wasDirtyParentViewBoundingRects)
5213 child->d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
5214 if (parentIgnoresVisible)
5215 child->d_ptr->ignoreVisible = 1;
5216 if (parentIgnoresOpacity)
5217 child->d_ptr->ignoreOpacity = 1;
5218 if (allChildrenDirty) {
5219 child->d_ptr->dirty = 1;
5220 child->d_ptr->fullUpdatePending = 1;
5221 child->d_ptr->dirtyChildren = 1;
5222 child->d_ptr->allChildrenDirty = 1;
5223 }
5224 processDirtyItemsRecursive(child, dirtyAncestorContainsChildren, opacity);
5225 }
5226
5227 if (itemClipsChildrenToShape) {
5228 // Reset updateClip.
5229 for (auto view : std::as_const(views))
5230 view->d_func()->setUpdateClip(nullptr);
5231 }
5232 } else if (wasDirtyParentSceneTransform) {
5233 item->d_ptr->invalidateChildrenSceneTransform();
5234 }
5235
5236 resetDirtyItem(item);
5237}
5238
5239/*!
5240 \deprecated
5241
5242 Paints the given \a items using the provided \a painter, after the
5243 background has been drawn, and before the foreground has been
5244 drawn. All painting is done in \e scene coordinates. Before
5245 drawing each item, the painter must be transformed using
5246 QGraphicsItem::sceneTransform().
5247
5248 The \a options parameter is the list of style option objects for
5249 each item in \a items. The \a numItems parameter is the number of
5250 items in \a items and options in \a options. The \a widget
5251 parameter is optional; if specified, it should point to the widget
5252 that is being painted on.
5253
5254 The default implementation prepares the painter matrix, and calls
5255 QGraphicsItem::paint() on all items. Reimplement this function to
5256 provide custom painting of all items for the scene; gaining
5257 complete control over how each item is drawn. In some cases this
5258 can increase drawing performance significantly.
5259
5260 Example:
5261
5262 \snippet graphicssceneadditem/graphicssceneadditemsnippet.cpp 0
5263
5264 Since Qt 4.6, this function is not called anymore unless
5265 the QGraphicsView::IndirectPainting flag is given as an Optimization
5266 flag.
5267
5268 \sa drawBackground(), drawForeground()
5269*/
5270void QGraphicsScene::drawItems(QPainter *painter,
5271 int numItems,
5272 QGraphicsItem *items[],
5273 const QStyleOptionGraphicsItem options[], QWidget *widget)
5274{
5275 Q_D(QGraphicsScene);
5276 // Make sure we don't have unpolished items before we draw.
5277 if (!d->unpolishedItems.isEmpty())
5278 d->_q_polishItems();
5279
5280 const qreal opacity = painter->opacity();
5281 QTransform viewTransform = painter->worldTransform();
5282 Q_UNUSED(options);
5283
5284 // Determine view, expose and flags.
5285 QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
5286 QRegion *expose = nullptr;
5287 const quint32 oldRectAdjust = d->rectAdjust;
5288 if (view) {
5289 d->updateAll = false;
5290 expose = &view->d_func()->exposedRegion;
5291 if (view->d_func()->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
5292 d->rectAdjust = 1;
5293 else
5294 d->rectAdjust = 2;
5295 }
5296
5297 // Find all toplevels, they are already sorted.
5298 QList<QGraphicsItem *> topLevelItems;
5299 for (int i = 0; i < numItems; ++i) {
5300 QGraphicsItem *item = items[i]->topLevelItem();
5301 if (!item->d_ptr->itemDiscovered) {
5302 topLevelItems << item;
5303 item->d_ptr->itemDiscovered = 1;
5304 d->drawSubtreeRecursive(item, painter, &viewTransform, expose, widget);
5305 }
5306 }
5307
5308 d->rectAdjust = oldRectAdjust;
5309 // Reset discovery bits.
5310 for (auto topLevelItem : std::as_const(topLevelItems))
5311 topLevelItem->d_ptr->itemDiscovered = 0;
5312
5313 painter->setWorldTransform(viewTransform);
5314 painter->setOpacity(opacity);
5315}
5316
5317/*!
5318 \since 4.4
5319
5320 Finds a new widget to give the keyboard focus to, as appropriate for Tab
5321 and Shift+Tab, and returns \c true if it can find a new widget, or false if
5322 it cannot. If \a next is true, this function searches forward; if \a next
5323 is false, it searches backward.
5324
5325 You can reimplement this function in a subclass of QGraphicsScene to
5326 provide fine-grained control over how tab focus passes inside your
5327 scene. The default implementation is based on the tab focus chain defined
5328 by QGraphicsWidget::setTabOrder().
5329*/
5330bool QGraphicsScene::focusNextPrevChild(bool next)
5331{
5332 Q_D(QGraphicsScene);
5333
5334 QGraphicsItem *item = focusItem();
5335 if (item && !item->isWidget()) {
5336 // Tab out of the scene.
5337 return false;
5338 }
5339 if (!item) {
5340 if (d->lastFocusItem && !d->lastFocusItem->isWidget()) {
5341 // Restore focus to the last focusable non-widget item that had
5342 // focus.
5343 setFocusItem(d->lastFocusItem, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
5344 return true;
5345 }
5346 if (d->activePanel) {
5347 if (d->activePanel->flags() & QGraphicsItem::ItemIsFocusable) {
5348 setFocusItem(d->activePanel, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
5349 return true;
5350 }
5351 if (d->activePanel->isWidget()) {
5352 QGraphicsWidget *test = static_cast<QGraphicsWidget *>(d->activePanel);
5353 QGraphicsWidget *fw = next ? test->d_func()->focusNext : test->d_func()->focusPrev;
5354 do {
5355 if (fw->focusPolicy() & Qt::TabFocus) {
5356 setFocusItem(fw, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
5357 return true;
5358 }
5359 fw = next ? fw->d_func()->focusNext : fw->d_func()->focusPrev;
5360 } while (fw != d->activePanel);
5361 }
5362 }
5363 }
5364 if (!item && !d->tabFocusFirst) {
5365 // No widgets...
5366 return false;
5367 }
5368
5369 // The item must be a widget.
5370 QGraphicsWidget *widget = nullptr;
5371 if (!item) {
5372 widget = next ? d->tabFocusFirst : d->tabFocusFirst->d_func()->focusPrev;
5373 } else {
5374 QGraphicsWidget *test = static_cast<QGraphicsWidget *>(item);
5375 widget = next ? test->d_func()->focusNext : test->d_func()->focusPrev;
5376 if (!widget->panel() && ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))) {
5377 // Tab out of the scene.
5378 return false;
5379 }
5380 }
5381 QGraphicsWidget *widgetThatHadFocus = widget;
5382
5383 // Run around the focus chain until we find a widget that can take tab focus.
5384 do {
5385 if (widget->flags() & QGraphicsItem::ItemIsFocusable
5386 && widget->isEnabled() && widget->isVisibleTo(nullptr)
5387 && (widget->focusPolicy() & Qt::TabFocus)
5388 && (!item || !item->isPanel() || item->isAncestorOf(widget))
5389 ) {
5390 setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
5391 return true;
5392 }
5393 widget = next ? widget->d_func()->focusNext : widget->d_func()->focusPrev;
5394 if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
5395 return false;
5396 } while (widget != widgetThatHadFocus);
5397
5398 return false;
5399}
5400
5401/*!
5402 \fn QGraphicsScene::changed(const QList<QRectF> &region)
5403
5404 This signal is emitted by QGraphicsScene when control reaches the
5405 event loop, if the scene content changes. The \a region parameter
5406 contains a list of scene rectangles that indicate the area that
5407 has been changed.
5408
5409 \sa QGraphicsView::updateScene()
5410*/
5411
5412/*!
5413 \fn QGraphicsScene::sceneRectChanged(const QRectF &rect)
5414
5415 This signal is emitted by QGraphicsScene whenever the scene rect changes.
5416 The \a rect parameter is the new scene rectangle.
5417
5418 \sa QGraphicsView::updateSceneRect()
5419*/
5420
5421/*!
5422 \fn QGraphicsScene::selectionChanged()
5423 \since 4.3
5424
5425 This signal is emitted by QGraphicsScene whenever the selection
5426 changes. You can call selectedItems() to get the new list of selected
5427 items.
5428
5429 The selection changes whenever an item is selected or unselected, a
5430 selection area is set, cleared or otherwise changed, if a preselected item
5431 is added to the scene, or if a selected item is removed from the scene.
5432
5433 QGraphicsScene emits this signal only once for group selection operations.
5434 For example, if you set a selection area, select or unselect a
5435 QGraphicsItemGroup, or if you add or remove from the scene a parent item
5436 that contains several selected items, selectionChanged() is emitted only
5437 once after the operation has completed (instead of once for each item).
5438
5439 \sa setSelectionArea(), selectedItems(), QGraphicsItem::setSelected()
5440*/
5441
5442/*!
5443 \fn void QGraphicsScene::focusItemChanged(QGraphicsItem *newFocusItem, QGraphicsItem *oldFocusItem, Qt::FocusReason reason)
5444
5445 This signal is emitted by QGraphicsScene whenever focus changes in the
5446 scene (i.e., when an item gains or loses input focus, or when focus
5447 passes from one item to another). You can connect to this signal if you
5448 need to keep track of when other items gain input focus. It is
5449 particularly useful for implementing virtual keyboards, input methods,
5450 and cursor items.
5451
5452 \a oldFocusItem is a pointer to the item that previously had focus, or
5453 0 if no item had focus before the signal was emitted. \a newFocusItem
5454 is a pointer to the item that gained input focus, or \nullptr if focus was lost.
5455 \a reason is the reason for the focus change (e.g., if the scene was
5456 deactivated while an input field had focus, \a oldFocusItem would point
5457 to the input field item, \a newFocusItem would be \nullptr, and \a reason
5458 would be Qt::ActiveWindowFocusReason.
5459*/
5460
5461/*!
5462 \since 4.4
5463
5464 Returns the scene's style, or the same as QApplication::style() if the
5465 scene has not been explicitly assigned a style.
5466
5467 \sa setStyle()
5468*/
5469QStyle *QGraphicsScene::style() const
5470{
5471 Q_D(const QGraphicsScene);
5472 // ### This function, and the use of styles in general, is non-reentrant.
5473 return d->style ? d->style : QApplication::style();
5474}
5475
5476/*!
5477 \since 4.4
5478
5479 Sets or replaces the style of the scene to \a style, and reparents the
5480 style to this scene. Any previously assigned style is deleted. The scene's
5481 style defaults to QApplication::style(), and serves as the default for all
5482 QGraphicsWidget items in the scene.
5483
5484 Changing the style, either directly by calling this function, or
5485 indirectly by calling QApplication::setStyle(), will automatically update
5486 the style for all widgets in the scene that do not have a style explicitly
5487 assigned to them.
5488
5489 If \a style is \nullptr, QGraphicsScene will revert to QApplication::style().
5490
5491 \sa style()
5492*/
5493void QGraphicsScene::setStyle(QStyle *style)
5494{
5495 Q_D(QGraphicsScene);
5496 // ### This function, and the use of styles in general, is non-reentrant.
5497 if (style == d->style)
5498 return;
5499
5500 // Delete the old style,
5501 delete d->style;
5502 if ((d->style = style))
5503 d->style->setParent(this);
5504
5505 // Notify the scene.
5506 QEvent event(QEvent::StyleChange);
5507 QCoreApplication::sendEvent(this, &event);
5508
5509 // Notify all widgets that don't have a style explicitly set.
5510 const auto items_ = items();
5511 for (QGraphicsItem *item : items_) {
5512 if (item->isWidget()) {
5513 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
5514 if (!widget->testAttribute(Qt::WA_SetStyle))
5515 QCoreApplication::sendEvent(widget, &event);
5516 }
5517 }
5518}
5519
5520/*!
5521 \property QGraphicsScene::font
5522 \since 4.4
5523 \brief the scene's default font
5524
5525 This property provides the scene's font. The scene font defaults to,
5526 and resolves all its entries from, QApplication::font.
5527
5528 If the scene's font changes, either directly through setFont() or
5529 indirectly when the application font changes, QGraphicsScene first
5530 sends itself a \l{QEvent::FontChange}{FontChange} event, and it then
5531 sends \l{QEvent::FontChange}{FontChange} events to all top-level
5532 widget items in the scene. These items respond by resolving their own
5533 fonts to the scene, and they then notify their children, who again
5534 notify their children, and so on, until all widget items have updated
5535 their fonts.
5536
5537 Changing the scene font, (directly or indirectly through
5538 QApplication::setFont(),) automatically schedules a redraw the entire
5539 scene.
5540
5541 \sa QWidget::font, QApplication::setFont(), palette, style()
5542*/
5543QFont QGraphicsScene::font() const
5544{
5545 Q_D(const QGraphicsScene);
5546 return d->font;
5547}
5548void QGraphicsScene::setFont(const QFont &font)
5549{
5550 Q_D(QGraphicsScene);
5551 QFont naturalFont = QApplication::font();
5552 naturalFont.setResolveMask(0);
5553 QFont resolvedFont = font.resolve(naturalFont);
5554 d->setFont_helper(resolvedFont);
5555}
5556
5557/*!
5558 \property QGraphicsScene::palette
5559 \since 4.4
5560 \brief the scene's default palette
5561
5562 This property provides the scene's palette. The scene palette defaults to,
5563 and resolves all its entries from, QApplication::palette.
5564
5565 If the scene's palette changes, either directly through setPalette() or
5566 indirectly when the application palette changes, QGraphicsScene first
5567 sends itself a \l{QEvent::PaletteChange}{PaletteChange} event, and it then
5568 sends \l{QEvent::PaletteChange}{PaletteChange} events to all top-level
5569 widget items in the scene. These items respond by resolving their own
5570 palettes to the scene, and they then notify their children, who again
5571 notify their children, and so on, until all widget items have updated
5572 their palettes.
5573
5574 Changing the scene palette, (directly or indirectly through
5575 QApplication::setPalette(),) automatically schedules a redraw the entire
5576 scene.
5577
5578 \sa QWidget::palette, QApplication::setPalette(), font, style()
5579*/
5580QPalette QGraphicsScene::palette() const
5581{
5582 Q_D(const QGraphicsScene);
5583 return d->palette;
5584}
5585void QGraphicsScene::setPalette(const QPalette &palette)
5586{
5587 Q_D(QGraphicsScene);
5588 QPalette naturalPalette = QGuiApplication::palette();
5589 naturalPalette.setResolveMask(0);
5590 QPalette resolvedPalette = palette.resolve(naturalPalette);
5591 d->setPalette_helper(resolvedPalette);
5592}
5593
5594/*!
5595 \since 4.6
5596
5597 Returns \c true if the scene is active (e.g., it's viewed by
5598 at least one QGraphicsView that is active); otherwise returns \c false.
5599
5600 \sa QGraphicsItem::isActive(), QWidget::isActiveWindow()
5601*/
5602bool QGraphicsScene::isActive() const
5603{
5604 Q_D(const QGraphicsScene);
5605 return d->activationRefCount > 0;
5606}
5607
5608/*!
5609 \since 4.6
5610 Returns the current active panel, or \nullptr if no panel is
5611 currently active.
5612
5613 \sa QGraphicsScene::setActivePanel()
5614*/
5615QGraphicsItem *QGraphicsScene::activePanel() const
5616{
5617 Q_D(const QGraphicsScene);
5618 return d->activePanel;
5619}
5620
5621/*!
5622 \since 4.6
5623 Activates \a item, which must be an item in this scene. You
5624 can also pass 0 for \a item, in which case QGraphicsScene will
5625 deactivate any currently active panel.
5626
5627 If the scene is currently inactive, \a item remains inactive until the
5628 scene becomes active (or, ir \a item is \nullptr, no item will be activated).
5629
5630 \sa activePanel(), isActive(), QGraphicsItem::isActive()
5631*/
5632void QGraphicsScene::setActivePanel(QGraphicsItem *item)
5633{
5634 Q_D(QGraphicsScene);
5635 d->setActivePanelHelper(item, false);
5636}
5637
5638/*!
5639 \since 4.4
5640
5641 Returns the current active window, or \nullptr if no window is
5642 currently active.
5643
5644 \sa QGraphicsScene::setActiveWindow()
5645*/
5646QGraphicsWidget *QGraphicsScene::activeWindow() const
5647{
5648 Q_D(const QGraphicsScene);
5649 if (d->activePanel && d->activePanel->isWindow())
5650 return static_cast<QGraphicsWidget *>(d->activePanel);
5651 return nullptr;
5652}
5653
5654/*!
5655 \since 4.4
5656 Activates \a widget, which must be a widget in this scene. You can also
5657 pass 0 for \a widget, in which case QGraphicsScene will deactivate any
5658 currently active window.
5659
5660 \sa activeWindow(), QGraphicsWidget::isActiveWindow()
5661*/
5662void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget)
5663{
5664 if (widget && widget->scene() != this) {
5665 qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene",
5666 widget);
5667 return;
5668 }
5669
5670 // Activate the widget's panel (all windows are panels).
5671 QGraphicsItem *panel = widget ? widget->panel() : nullptr;
5672 setActivePanel(panel);
5673
5674 // Raise
5675 if (panel) {
5676 QGraphicsItem *parent = panel->parentItem();
5677 // Raise ### inefficient for toplevels
5678
5679 // Find the highest z value.
5680 qreal z = panel->zValue();
5681 const auto siblings = parent ? parent->childItems() : items();
5682 for (QGraphicsItem *sibling : siblings) {
5683 if (sibling != panel && sibling->isWindow())
5684 z = qMax(z, sibling->zValue());
5685 }
5686
5687 // This will probably never overflow.
5688 const qreal litt = qreal(0.001);
5689 panel->setZValue(z + litt);
5690 }
5691}
5692
5693/*!
5694 \since 4.6
5695
5696 Sends event \a event to item \a item through possible event filters.
5697
5698 The event is sent only if the item is enabled.
5699
5700 Returns \c false if the event was filtered or if the item is disabled.
5701 Otherwise returns the value that was returned from the event handler.
5702
5703 \sa QGraphicsItem::sceneEvent(), QGraphicsItem::sceneEventFilter()
5704*/
5705bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event)
5706{
5707 Q_D(QGraphicsScene);
5708 if (!item) {
5709 qWarning("QGraphicsScene::sendEvent: cannot send event to a null item");
5710 return false;
5711 }
5712 if (item->scene() != this) {
5713 qWarning("QGraphicsScene::sendEvent: item %p's scene (%p)"
5714 " is different from this scene (%p)",
5715 item, item->scene(), this);
5716 return false;
5717 }
5718 return d->sendEvent(item, event);
5719}
5720
5721/*!
5722 \property QGraphicsScene::minimumRenderSize
5723 \since 5.4
5724 \brief the minimal view-transformed size an item must have to be drawn
5725
5726 When the scene is rendered, any item whose width or height, transformed
5727 to the target view, is smaller that minimumRenderSize(), will not be
5728 rendered. If an item is not rendered and it clips its children items
5729 they will also not be rendered. Set this value to speed up rendering
5730 of scenes with many objects rendered on a zoomed out view.
5731
5732 The default value is 0. If unset, or if set to 0 or a negative value,
5733 all items will always be rendered.
5734
5735 For example, setting this property can be especially useful if a scene
5736 is rendered by multiple views, one of which serves as an overview which
5737 always displays all items. In scenes with many items, such a view will
5738 use a high scaling factor so that all items can be shown. Due to the
5739 scaling, smaller items will only make an insignificant contribution to
5740 the final rendered scene. To avoid drawing these items and reduce the
5741 time necessary to render the scene, you can call setMinimumRenderSize()
5742 with a non-negative value.
5743
5744 \note Items that are not drawn as a result of being too small, are still
5745 returned by methods such as items() and itemAt(), and participate in
5746 collision detection and interactions. It is recommended that you set
5747 minimumRenderSize() to a value less than or equal to 1 in order to
5748 avoid large unrendered items that are interactive.
5749
5750 \sa QStyleOptionGraphicsItem::levelOfDetailFromTransform()
5751*/
5752qreal QGraphicsScene::minimumRenderSize() const
5753{
5754 Q_D(const QGraphicsScene);
5755 return d->minimumRenderSize;
5756}
5757void QGraphicsScene::setMinimumRenderSize(qreal minSize)
5758{
5759 Q_D(QGraphicsScene);
5760 d->minimumRenderSize = minSize;
5761 update();
5762}
5763
5764/*!
5765 \property QGraphicsScene::focusOnTouch
5766 \since 5.12
5767 \brief whether items gain focus when receiving a \e {touch begin} event.
5768
5769 The usual behavior is to transfer focus only when an item is clicked. Often
5770 a tap on a touchpad is interpreted as equivalent to a mouse click by the
5771 operating system, generating a synthesized click event in response. However,
5772 at least on macOS you can configure this behavior.
5773
5774 By default, QGraphicsScene also transfers focus when you touch on a trackpad
5775 or similar. If the operating system is configured to not generate a
5776 synthetic mouse click on tapping the trackpad, this is surprising. If the
5777 operating system does generate synthetic mouse clicks on tapping the
5778 trackpad, the focus transfer on starting a touch gesture is unnecessary.
5779
5780 With focusOnTouch switched off, QGraphicsScene behaves as one would expect
5781 on macOS.
5782
5783 The default value is \c true, ensuring that the default behavior is just as
5784 in Qt versions prior to 5.12. Set to \c false to prevent touch events from
5785 triggering focus changes.
5786*/
5787bool QGraphicsScene::focusOnTouch() const
5788{
5789 Q_D(const QGraphicsScene);
5790 return d->focusOnTouch;
5791}
5792
5793void QGraphicsScene::setFocusOnTouch(bool enabled)
5794{
5795 Q_D(QGraphicsScene);
5796 d->focusOnTouch = enabled;
5797}
5798
5799void QGraphicsScenePrivate::addView(QGraphicsView *view)
5800{
5801 views << view;
5802#ifndef QT_NO_GESTURES
5803 for (auto it = grabbedGestures.constBegin();
5804 it != grabbedGestures.constEnd(); ++it)
5805 view->viewport()->grabGesture(it.key());
5806#endif
5807}
5808
5809void QGraphicsScenePrivate::removeView(QGraphicsView *view)
5810{
5811 views.removeAll(view);
5812}
5813
5814void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent)
5815{
5816 const QTransform mapFromScene =
5817 item->d_ptr->genericMapFromSceneTransform(static_cast<const QWidget *>(touchEvent->target()));
5818
5819 for (int i = 0; i < touchEvent->pointCount(); ++i) {
5820 auto &pt = touchEvent->point(i);
5821 QMutableEventPoint::setPosition(pt, mapFromScene.map(pt.scenePosition()));
5822 }
5823}
5824
5825int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos)
5826{
5827 int closestTouchPointId = -1;
5828 qreal closestDistance = qreal(0.);
5829 for (const QEventPoint &touchPoint : std::as_const(sceneCurrentTouchPoints)) {
5830 qreal distance = QLineF(scenePos, touchPoint.scenePosition()).length();
5831 if (closestTouchPointId == -1|| distance < closestDistance) {
5832 closestTouchPointId = touchPoint.id();
5833 closestDistance = distance;
5834 }
5835 }
5836 return closestTouchPointId;
5837}
5838
5839void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent)
5840{
5841 typedef std::pair<QEventPoint::States, QList<QEventPoint> > StatesAndTouchPoints;
5842 QHash<QGraphicsItem *, StatesAndTouchPoints> itemsNeedingEvents;
5843
5844 const auto &touchPoints = sceneTouchEvent->points();
5845 for (const auto &touchPoint : touchPoints) {
5846 // update state
5847 QGraphicsItem *item = nullptr;
5848 if (touchPoint.state() == QEventPoint::State::Pressed) {
5849 if (sceneTouchEvent->pointingDevice()->type() == QInputDevice::DeviceType::TouchPad) {
5850 // on touch-pad devices, send all touch points to the same item
5851 item = itemForTouchPointId.isEmpty()
5852 ? 0
5853 : itemForTouchPointId.constBegin().value();
5854 }
5855
5856 if (!item) {
5857 // determine which item this touch point will go to
5858 cachedItemsUnderMouse = itemsAtPosition(touchPoint.globalPosition().toPoint(),
5859 touchPoint.scenePosition(),
5860 static_cast<QWidget *>(sceneTouchEvent->target()));
5861 item = cachedItemsUnderMouse.isEmpty() ? 0 : cachedItemsUnderMouse.constFirst();
5862 }
5863
5864 if (sceneTouchEvent->pointingDevice()->type() == QInputDevice::DeviceType::TouchScreen) {
5865 // on touch-screens, combine this touch point with the closest one we find
5866 int closestTouchPointId = findClosestTouchPointId(touchPoint.scenePosition());
5867 QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPointId);
5868 if (!item || (closestItem && cachedItemsUnderMouse.contains(closestItem)))
5869 item = closestItem;
5870 }
5871 if (!item)
5872 continue;
5873
5874 itemForTouchPointId.insert(touchPoint.id(), item);
5875 sceneCurrentTouchPoints.insert(touchPoint.id(), touchPoint);
5876 } else if (touchPoint.state() == QEventPoint::State::Released) {
5877 item = itemForTouchPointId.take(touchPoint.id());
5878 if (!item)
5879 continue;
5880
5881 sceneCurrentTouchPoints.remove(touchPoint.id());
5882 } else {
5883 item = itemForTouchPointId.value(touchPoint.id());
5884 if (!item)
5885 continue;
5886 Q_ASSERT(sceneCurrentTouchPoints.contains(touchPoint.id()));
5887 sceneCurrentTouchPoints[touchPoint.id()] = touchPoint;
5888 }
5889
5890 StatesAndTouchPoints &statesAndTouchPoints = itemsNeedingEvents[item];
5891 statesAndTouchPoints.first = QEventPoint::States(statesAndTouchPoints.first | touchPoint.state());
5892 statesAndTouchPoints.second.append(touchPoint);
5893 }
5894
5895 if (itemsNeedingEvents.isEmpty()) {
5896 sceneTouchEvent->ignore();
5897 return;
5898 }
5899
5900 bool ignoreSceneTouchEvent = true;
5901 QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator it = itemsNeedingEvents.constBegin();
5902 const QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator end = itemsNeedingEvents.constEnd();
5903 for (; it != end; ++it) {
5904 QGraphicsItem *item = it.key();
5905
5906 (void) item->isBlockedByModalPanel(&item);
5907
5908 // determine event type from the state mask
5909 QEvent::Type eventType;
5910 switch (it.value().first) {
5911 case QEventPoint::State::Pressed:
5912 // all touch points have pressed state
5913 eventType = QEvent::TouchBegin;
5914 break;
5915 case QEventPoint::State::Released:
5916 // all touch points have released state
5917 eventType = QEvent::TouchEnd;
5918 break;
5919 case QEventPoint::State::Stationary:
5920 // don't send the event if nothing changed
5921 continue;
5922 default:
5923 // all other combinations
5924 eventType = QEvent::TouchUpdate;
5925 break;
5926 }
5927
5928 QMutableTouchEvent touchEvent(eventType, sceneTouchEvent->pointingDevice(), sceneTouchEvent->modifiers(), it.value().second);
5929 touchEvent.setTarget(sceneTouchEvent->target());
5930 touchEvent.setModifiers(sceneTouchEvent->modifiers());
5931 touchEvent.setTimestamp(sceneTouchEvent->timestamp());
5932
5933 switch (touchEvent.type()) {
5934 case QEvent::TouchBegin:
5935 {
5936 // if the TouchBegin handler recurses, we assume that means the event
5937 // has been implicitly accepted and continue to send touch events
5938 item->d_ptr->acceptedTouchBeginEvent = true;
5939 bool res = sendTouchBeginEvent(item, &touchEvent) && touchEvent.isAccepted();
5940 if (!res) {
5941 // forget about these touch points, we didn't handle them
5942 const auto &unhandledTouchPoints = touchEvent.points();
5943 for (const auto &touchPoint : unhandledTouchPoints) {
5944 itemForTouchPointId.remove(touchPoint.id());
5945 sceneCurrentTouchPoints.remove(touchPoint.id());
5946 }
5947 ignoreSceneTouchEvent = false;
5948 }
5949 break;
5950 }
5951 default:
5952 if (item->d_ptr->acceptedTouchBeginEvent) {
5953 updateTouchPointsForItem(item, &touchEvent);
5954 (void) sendEvent(item, &touchEvent);
5955 ignoreSceneTouchEvent = false;
5956 }
5957 break;
5958 }
5959 }
5960 // don't override the acceptance state of the individual points
5961 sceneTouchEvent->QInputEvent::setAccepted(ignoreSceneTouchEvent);
5962}
5963
5964bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEvent *touchEvent)
5965{
5966 Q_Q(QGraphicsScene);
5967
5968 if (focusOnTouch) {
5969 if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.constFirst() != origin) {
5970 const QEventPoint &firstTouchPoint = touchEvent->points().first();
5971 cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.globalPosition().toPoint(),
5972 firstTouchPoint.scenePosition(),
5973 static_cast<QWidget *>(touchEvent->target()));
5974 }
5975
5976 // Set focus on the topmost enabled item that can take focus.
5977 bool setFocus = false;
5978
5979 for (QGraphicsItem *item : std::as_const(cachedItemsUnderMouse)) {
5980 if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
5981 if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
5982 setFocus = true;
5983 if (item != q->focusItem())
5984 q->setFocusItem(item, Qt::MouseFocusReason);
5985 break;
5986 }
5987 }
5988 if (item->isPanel())
5989 break;
5990 if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation)
5991 break;
5992 if (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling) {
5993 // Make sure we don't clear focus.
5994 setFocus = true;
5995 break;
5996 }
5997 }
5998
5999 // If nobody could take focus, clear it.
6000 if (!stickyFocus && !setFocus)
6001 q->setFocusItem(nullptr, Qt::MouseFocusReason);
6002 }
6003
6004 bool res = false;
6005 bool eventAccepted = touchEvent->isAccepted();
6006 for (QGraphicsItem *item : std::as_const(cachedItemsUnderMouse)) {
6007 // first, try to deliver the touch event
6008 updateTouchPointsForItem(item, touchEvent);
6009 bool acceptTouchEvents = item->acceptTouchEvents();
6010 touchEvent->setAccepted(acceptTouchEvents);
6011 res = acceptTouchEvents && sendEvent(item, touchEvent);
6012 eventAccepted = touchEvent->isAccepted();
6013 if (itemForTouchPointId.value(touchEvent->points().first().id()) == 0) {
6014 // item was deleted
6015 item = nullptr;
6016 } else {
6017 item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted);
6018 }
6019 touchEvent->m_spont = false;
6020 if (res && eventAccepted) {
6021 // the first item to accept the TouchBegin gets an implicit grab.
6022 const auto &touchPoints = touchEvent->points();
6023 for (const auto &touchPoint : touchPoints)
6024 itemForTouchPointId[touchPoint.id()] = item; // can be zero
6025 break;
6026 }
6027 if (item && item->isPanel())
6028 break;
6029 }
6030
6031 // don't override the acceptance state of the touch points
6032 touchEvent->QInputEvent::setAccepted(eventAccepted);
6033 return res;
6034}
6035
6036void QGraphicsScenePrivate::enableTouchEventsOnViews()
6037{
6038 for (QGraphicsView *view : std::as_const(views))
6039 view->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
6040}
6041
6042void QGraphicsScenePrivate::updateInputMethodSensitivityInViews()
6043{
6044 for (auto view : std::as_const(views))
6045 view->d_func()->updateInputMethodSensitivity();
6046}
6047
6048void QGraphicsScenePrivate::enterModal(QGraphicsItem *panel, QGraphicsItem::PanelModality previousModality)
6049{
6050 Q_Q(QGraphicsScene);
6051 Q_ASSERT(panel && panel->isPanel());
6052
6053 QGraphicsItem::PanelModality panelModality = panel->d_ptr->panelModality;
6054 if (previousModality != QGraphicsItem::NonModal) {
6055 // the panel is changing from one modality type to another... temporarily set it back so
6056 // that blockedPanels is populated correctly
6057 panel->d_ptr->panelModality = previousModality;
6058 }
6059
6060 QSet<QGraphicsItem *> blockedPanels;
6061 {
6062 const auto items_ = q->items();
6063 for (const auto &item : items_) {
6064 if (item->isPanel() && item->isBlockedByModalPanel())
6065 blockedPanels.insert(item);
6066 }
6067 }
6068 // blockedPanels contains all currently blocked panels
6069
6070 if (previousModality != QGraphicsItem::NonModal) {
6071 // reset the modality to the proper value, since we changed it above
6072 panel->d_ptr->panelModality = panelModality;
6073 // remove this panel so that it will be reinserted at the front of the stack
6074 modalPanels.removeAll(panel);
6075 }
6076
6077 modalPanels.prepend(panel);
6078
6079 if (!hoverItems.isEmpty()) {
6080 // send GraphicsSceneHoverLeave events to newly blocked hoverItems
6081 QGraphicsSceneHoverEvent hoverEvent;
6082 hoverEvent.setScenePos(lastSceneMousePos);
6083 dispatchHoverEvent(&hoverEvent);
6084 }
6085
6086 if (!mouseGrabberItems.isEmpty() && lastMouseGrabberItemHasImplicitMouseGrab) {
6087 QGraphicsItem *item = mouseGrabberItems.constLast();
6088 if (item->isBlockedByModalPanel())
6089 ungrabMouse(item, /*itemIsDying =*/ false);
6090 }
6091
6092 QEvent windowBlockedEvent(QEvent::WindowBlocked);
6093 QEvent windowUnblockedEvent(QEvent::WindowUnblocked);
6094 const auto items_ = q->items();
6095 for (const auto &item : items_) {
6096 if (item->isPanel()) {
6097 if (!blockedPanels.contains(item) && item->isBlockedByModalPanel()) {
6098 // send QEvent::WindowBlocked to newly blocked panels
6099 sendEvent(item, &windowBlockedEvent);
6100 } else if (blockedPanels.contains(item) && !item->isBlockedByModalPanel()) {
6101 // send QEvent::WindowUnblocked to unblocked panels when downgrading
6102 // a panel from SceneModal to PanelModal
6103 sendEvent(item, &windowUnblockedEvent);
6104 }
6105 }
6106 }
6107}
6108
6109void QGraphicsScenePrivate::leaveModal(QGraphicsItem *panel)
6110{
6111 Q_Q(QGraphicsScene);
6112 Q_ASSERT(panel && panel->isPanel());
6113
6114 QSet<QGraphicsItem *> blockedPanels;
6115 {
6116 const auto items_ = q->items();
6117 for (const auto &item : items_) {
6118 if (item->isPanel() && item->isBlockedByModalPanel())
6119 blockedPanels.insert(item);
6120 }
6121 }
6122
6123 modalPanels.removeAll(panel);
6124
6125 {
6126 QEvent e(QEvent::WindowUnblocked);
6127 const auto items_ = q->items();
6128 for (const auto &item : items_) {
6129 if (item->isPanel() && blockedPanels.contains(item) && !item->isBlockedByModalPanel())
6130 sendEvent(item, &e);
6131 }
6132 }
6133
6134 // send GraphicsSceneHoverEnter events to newly unblocked items
6135 QGraphicsSceneHoverEvent hoverEvent;
6136 hoverEvent.setScenePos(lastSceneMousePos);
6137 dispatchHoverEvent(&hoverEvent);
6138}
6139
6140#ifndef QT_NO_GESTURES
6141void QGraphicsScenePrivate::gestureTargetsAtHotSpots(const QSet<QGesture *> &gestures,
6142 Qt::GestureFlag flag,
6143 QHash<QGraphicsObject *, QSet<QGesture *> > *targets,
6144 QSet<QGraphicsObject *> *itemsSet,
6145 QSet<QGesture *> *normal,
6146 QSet<QGesture *> *conflicts)
6147{
6148 QSet<QGesture *> normalGestures; // that are not in conflicted state.
6149 for (QGesture *gesture : gestures) {
6150 if (!gesture->hasHotSpot())
6151 continue;
6152 const Qt::GestureType gestureType = gesture->gestureType();
6153 const QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), gesture->d_func()->sceneHotSpot, nullptr);
6154 for (int j = 0; j < items.size(); ++j) {
6155 QGraphicsItem *item = items.at(j);
6156
6157 // Check if the item is blocked by a modal panel and use it as
6158 // a target instead of this item.
6159 (void) item->isBlockedByModalPanel(&item);
6160
6161 if (QGraphicsObject *itemobj = item->toGraphicsObject()) {
6162 QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
6163 QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator it =
6164 d->gestureContext.constFind(gestureType);
6165 if (it != d->gestureContext.constEnd() && (!flag || (it.value() & flag))) {
6166 if (normalGestures.contains(gesture)) {
6167 normalGestures.remove(gesture);
6168 if (conflicts)
6169 conflicts->insert(gesture);
6170 } else {
6171 normalGestures.insert(gesture);
6172 }
6173 if (targets)
6174 (*targets)[itemobj].insert(gesture);
6175 if (itemsSet)
6176 (*itemsSet).insert(itemobj);
6177 }
6178 }
6179 // Don't propagate through panels.
6180 if (item->isPanel())
6181 break;
6182 }
6183 }
6184 if (normal)
6185 *normal = normalGestures;
6186}
6187
6188void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event)
6189{
6190 QWidget *viewport = event->widget();
6191 if (!viewport)
6192 return;
6193 QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(viewport->parent());
6194 if (!graphicsView)
6195 return;
6196
6197 const QList<QGesture *> allGestures = event->gestures();
6198 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6199 << "Gestures:" << allGestures;
6200
6201 QSet<QGesture *> startedGestures;
6202 QPoint delta = viewport->mapFromGlobal(QPoint());
6203 QTransform toScene = QTransform::fromTranslate(delta.x(), delta.y())
6204 * graphicsView->viewportTransform().inverted();
6205 for (QGesture *gesture : allGestures) {
6206 // cache scene coordinates of the hot spot
6207 if (gesture->hasHotSpot()) {
6208 gesture->d_func()->sceneHotSpot = toScene.map(gesture->hotSpot());
6209 } else {
6210 gesture->d_func()->sceneHotSpot = QPointF();
6211 }
6212
6213 QGraphicsObject *target = gestureTargets.value(gesture, 0);
6214 if (!target) {
6215 // when we are not in started mode but don't have a target
6216 // then the only one interested in gesture is the view/scene
6217 if (gesture->state() == Qt::GestureStarted)
6218 startedGestures.insert(gesture);
6219 }
6220 }
6221
6222 if (!startedGestures.isEmpty()) {
6223 QSet<QGesture *> normalGestures; // that have just one target
6224 QSet<QGesture *> conflictedGestures; // that have multiple possible targets
6225 gestureTargetsAtHotSpots(startedGestures, Qt::GestureFlag(0), &cachedItemGestures, nullptr,
6226 &normalGestures, &conflictedGestures);
6227 cachedTargetItems = cachedItemGestures.keys();
6228 std::sort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
6229 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6230 << "Normal gestures:" << normalGestures
6231 << "Conflicting gestures:" << conflictedGestures;
6232
6233 // deliver conflicted gestures as override events AND remember
6234 // initial gesture targets
6235 if (!conflictedGestures.isEmpty()) {
6236 for (int i = 0; i < cachedTargetItems.size(); ++i) {
6237 QPointer<QGraphicsObject> item = cachedTargetItems.at(i);
6238
6239 // get gestures to deliver to the current item
6240 const QSet<QGesture *> gestures = conflictedGestures & cachedItemGestures.value(item.data());
6241 if (gestures.isEmpty())
6242 continue;
6243
6244 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6245 << "delivering override to"
6246 << item.data() << gestures;
6247 // send gesture override
6248 QGestureEvent ev(gestures.values());
6249 ev.t = QEvent::GestureOverride;
6250 ev.setWidget(event->widget());
6251 // mark event and individual gestures as ignored
6252 ev.ignore();
6253 for (QGesture *g : gestures)
6254 ev.setAccepted(g, false);
6255 sendEvent(item.data(), &ev);
6256 // mark all accepted gestures to deliver them as normal gesture events
6257 for (QGesture *g : gestures) {
6258 if (ev.isAccepted() || ev.isAccepted(g)) {
6259 conflictedGestures.remove(g);
6260 // mark the item as a gesture target
6261 if (item) {
6262 gestureTargets.insert(g, item.data());
6263 QHash<QGraphicsObject *, QSet<QGesture *> >::iterator it, e;
6264 it = cachedItemGestures.begin();
6265 e = cachedItemGestures.end();
6266 for(; it != e; ++it)
6267 it.value().remove(g);
6268 cachedItemGestures[item.data()].insert(g);
6269 }
6270 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6271 << "override was accepted:"
6272 << g << item.data();
6273 }
6274 // remember the first item that received the override event
6275 // as it most likely become a target if no one else accepts
6276 // the override event
6277 if (!gestureTargets.contains(g) && item)
6278 gestureTargets.insert(g, item.data());
6279
6280 }
6281 if (conflictedGestures.isEmpty())
6282 break;
6283 }
6284 }
6285 // remember the initial target item for each gesture that was not in
6286 // the conflicted state.
6287 if (!normalGestures.isEmpty()) {
6288 for (int i = 0; i < cachedTargetItems.size() && !normalGestures.isEmpty(); ++i) {
6289 QGraphicsObject *item = cachedTargetItems.at(i);
6290
6291 // get gestures to deliver to the current item
6292 const auto gestures = cachedItemGestures.value(item);
6293 for (QGesture *g : gestures) {
6294 if (!gestureTargets.contains(g)) {
6295 gestureTargets.insert(g, item);
6296 normalGestures.remove(g);
6297 }
6298 }
6299 }
6300 }
6301 }
6302
6303
6304 // deliver all gesture events
6305 QSet<QGesture *> undeliveredGestures;
6306 QSet<QGesture *> parentPropagatedGestures;
6307 for (QGesture *gesture : allGestures) {
6308 if (QGraphicsObject *target = gestureTargets.value(gesture, 0)) {
6309 cachedItemGestures[target].insert(gesture);
6310 cachedTargetItems.append(target);
6311 undeliveredGestures.insert(gesture);
6312 QGraphicsItemPrivate *d = target->QGraphicsItem::d_func();
6313 const Qt::GestureFlags flags = d->gestureContext.value(gesture->gestureType());
6314 if (flags & Qt::IgnoredGesturesPropagateToParent)
6315 parentPropagatedGestures.insert(gesture);
6316 } else {
6317 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6318 << "no target for" << gesture << "at"
6319 << gesture->hotSpot() << gesture->d_func()->sceneHotSpot;
6320 }
6321 }
6322 std::sort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
6323 for (int i = 0; i < cachedTargetItems.size(); ++i) {
6324 QPointer<QGraphicsObject> receiver = cachedTargetItems.at(i);
6325 const QSet<QGesture *> gestures = (undeliveredGestures
6326 & cachedItemGestures.value(receiver.data()))
6327 - cachedAlreadyDeliveredGestures.value(receiver.data());
6328
6329 if (gestures.isEmpty())
6330 continue;
6331
6332 cachedAlreadyDeliveredGestures[receiver.data()] += gestures;
6333 const bool isPanel = receiver.data()->isPanel();
6334
6335 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6336 << "delivering to"
6337 << receiver.data() << gestures;
6338 QGestureEvent ev(gestures.values());
6339 ev.setWidget(event->widget());
6340 sendEvent(receiver.data(), &ev);
6341 QSet<QGesture *> ignoredGestures;
6342 for (QGesture *g : gestures) {
6343 if (!ev.isAccepted() && !ev.isAccepted(g)) {
6344 // if the gesture was ignored by its target, we will update the
6345 // targetItems list with a possible target items (items that
6346 // want to receive partial gestures).
6347 // ### won't work if the target was destroyed in the event
6348 // we will just stop delivering it.
6349 if (receiver && receiver.data() == gestureTargets.value(g, 0))
6350 ignoredGestures.insert(g);
6351 } else {
6352 if (receiver && g->state() == Qt::GestureStarted) {
6353 // someone accepted the propagated initial GestureStarted
6354 // event, let it be the new target for all following events.
6355 gestureTargets[g] = receiver.data();
6356 }
6357 undeliveredGestures.remove(g);
6358 }
6359 }
6360 if (undeliveredGestures.isEmpty())
6361 break;
6362
6363 // ignoredGestures list is only filled when delivering to the gesture
6364 // target item, so it is safe to assume item == target.
6365 if (!ignoredGestures.isEmpty() && !isPanel) {
6366 // look for new potential targets for gestures that were ignored
6367 // and should be propagated.
6368
6369 QSet<QGraphicsObject *> targetsSet(cachedTargetItems.constBegin(), cachedTargetItems.constEnd());
6370
6371 if (receiver) {
6372 // first if the gesture should be propagated to parents only
6373 for (QSet<QGesture *>::iterator it = ignoredGestures.begin();
6374 it != ignoredGestures.end();) {
6375 if (parentPropagatedGestures.contains(*it)) {
6376 QGesture *gesture = *it;
6377 const Qt::GestureType gestureType = gesture->gestureType();
6378 QGraphicsItem *item = receiver.data();
6379 while (item) {
6380 if (QGraphicsObject *obj = item->toGraphicsObject()) {
6381 if (item->d_func()->gestureContext.contains(gestureType)) {
6382 targetsSet.insert(obj);
6383 cachedItemGestures[obj].insert(gesture);
6384 }
6385 }
6386 if (item->isPanel())
6387 break;
6388 item = item->parentItem();
6389 }
6390
6391 it = ignoredGestures.erase(it);
6392 continue;
6393 }
6394 ++it;
6395 }
6396 }
6397
6398 gestureTargetsAtHotSpots(ignoredGestures, Qt::ReceivePartialGestures,
6399 &cachedItemGestures, &targetsSet, nullptr, nullptr);
6400
6401 cachedTargetItems = targetsSet.values();
6402 std::sort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
6403 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6404 << "new targets:" << cachedTargetItems;
6405 i = -1; // start delivery again
6406 continue;
6407 }
6408 }
6409
6410 for (QGesture *g : std::as_const(startedGestures)) {
6411 if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
6412 DEBUG() << "lets try to cancel some";
6413 // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
6414 cancelGesturesForChildren(g);
6415 }
6416 }
6417
6418 // forget about targets for gestures that have ended
6419 for (QGesture *g : allGestures) {
6420 switch (g->state()) {
6421 case Qt::GestureFinished:
6422 case Qt::GestureCanceled:
6423 gestureTargets.remove(g);
6424 break;
6425 default:
6426 break;
6427 }
6428 }
6429
6430 cachedTargetItems.clear();
6431 cachedItemGestures.clear();
6432 cachedAlreadyDeliveredGestures.clear();
6433}
6434
6435void QGraphicsScenePrivate::cancelGesturesForChildren(QGesture *original)
6436{
6437 Q_ASSERT(original);
6438 QGraphicsItem *originalItem = gestureTargets.value(original);
6439 if (originalItem == nullptr) // we only act on accepted gestures, which implies it has a target.
6440 return;
6441
6442 // iterate over all active gestures and for each find the owner
6443 // if the owner is part of our sub-hierarchy, cancel it.
6444
6445 QSet<QGesture *> canceledGestures;
6446 QHash<QGesture *, QGraphicsObject *>::Iterator iter = gestureTargets.begin();
6447 while (iter != gestureTargets.end()) {
6448 QGraphicsObject *item = iter.value();
6449 // note that we don't touch the gestures for our originalItem
6450 if (item != originalItem && originalItem->isAncestorOf(item)) {
6451 DEBUG() << " found a gesture to cancel" << iter.key();
6452 iter.key()->d_func()->state = Qt::GestureCanceled;
6453 canceledGestures << iter.key();
6454 }
6455 ++iter;
6456 }
6457
6458 // sort them per target item by cherry picking from almostCanceledGestures and delivering
6459 QSet<QGesture *> almostCanceledGestures = canceledGestures;
6460 QSet<QGesture *>::Iterator setIter;
6461 while (!almostCanceledGestures.isEmpty()) {
6462 QGraphicsObject *target = nullptr;
6463 QSet<QGesture*> gestures;
6464 setIter = almostCanceledGestures.begin();
6465 // sort per target item
6466 while (setIter != almostCanceledGestures.end()) {
6467 QGraphicsObject *item = gestureTargets.value(*setIter);
6468 if (target == nullptr)
6469 target = item;
6470 if (target == item) {
6471 gestures << *setIter;
6472 setIter = almostCanceledGestures.erase(setIter);
6473 } else {
6474 ++setIter;
6475 }
6476 }
6477 Q_ASSERT(target);
6478
6479 const QList<QGesture *> list = gestures.values();
6480 QGestureEvent ev(list);
6481 sendEvent(target, &ev);
6482
6483 if (!ev.isAccepted()) {
6484 for (QGesture *g : list) {
6485
6486 if (ev.isAccepted(g))
6487 continue;
6488
6489 if (!g->hasHotSpot())
6490 continue;
6491
6492 const QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), g->d_func()->sceneHotSpot, nullptr);
6493 for (const auto &item : items) {
6494 QGraphicsObject *object = item->toGraphicsObject();
6495 if (!object)
6496 continue;
6497 QGraphicsItemPrivate *d = object->QGraphicsItem::d_func();
6498 if (d->gestureContext.contains(g->gestureType())) {
6499 QList<QGesture *> list;
6500 list << g;
6501 QGestureEvent ev(list);
6502 sendEvent(object, &ev);
6503 if (ev.isAccepted() || ev.isAccepted(g))
6504 break; // successfully delivered
6505 }
6506 }
6507 }
6508 }
6509 }
6510
6511 QGestureManager *gestureManager = QApplicationPrivate::instance()->gestureManager;
6512 Q_ASSERT(gestureManager); // it would be very odd if we got called without a manager.
6513 for (setIter = canceledGestures.begin(); setIter != canceledGestures.end(); ++setIter) {
6514 gestureManager->recycle(*setIter);
6515 gestureTargets.remove(*setIter);
6516 }
6517}
6518
6519void QGraphicsScenePrivate::grabGesture(QGraphicsItem *, Qt::GestureType gesture)
6520{
6521 (void)QGestureManager::instance(); // create a gesture manager
6522 if (!grabbedGestures[gesture]++) {
6523 for (QGraphicsView *view : std::as_const(views))
6524 view->viewport()->grabGesture(gesture);
6525 }
6526}
6527
6528void QGraphicsScenePrivate::ungrabGesture(QGraphicsItem *item, Qt::GestureType gesture)
6529{
6530 // we know this can only be an object
6531 Q_ASSERT(item->d_ptr->isObject);
6532 QGraphicsObject *obj = static_cast<QGraphicsObject *>(item);
6533 QGestureManager::instance()->cleanupCachedGestures(obj, gesture);
6534 if (!--grabbedGestures[gesture]) {
6535 for (QGraphicsView *view : std::as_const(views))
6536 view->viewport()->ungrabGesture(gesture);
6537 }
6538}
6539#endif // QT_NO_GESTURES
6540
6541QT_END_NAMESPACE
6542
6543#include "moc_qgraphicsscene.cpp"
friend class QPainter
friend class QWidget
Definition qpainter.h:431
Combined button and popup list for selecting options.
bool qt_sendSpontaneousEvent(QObject *, QEvent *)
static void setClip(QPainter *painter, QGraphicsItem *item)
static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
static void setWorldTransform(QPainter *painter, const QTransform *const transformPtr, const QTransform *effectTransform)
static bool transformIsSimple(const QTransform &transform)
#define ENSURE_TRANSFORM_PTR
static void _q_paintItem(QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, bool useWindowOpacity, bool painterStateProtection)
static bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item, const QRectF &rect, bool itemIsUntransformable)
static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &pixmapExposed, const QTransform &itemToPixmap, QPainter::RenderHints renderHints, const QStyleOptionGraphicsItem *option, bool painterStateProtection)