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

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