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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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