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

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