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

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