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