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
qquickoverlay.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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
10#include "qquickdrawer_p.h"
13#include <QtGui/qpainterpath.h>
14#include <QtQml/qqmlinfo.h>
15#include <QtQml/qqmlproperty.h>
16#include <QtQml/qqmlcomponent.h>
17#if QT_CONFIG(quick_shadereffect)
18#include <QtQuick/private/qquickshadereffectsource_p.h>
19#endif
20#include <algorithm>
21
22QT_BEGIN_NAMESPACE
23
24/*!
25 \qmltype Overlay
26 \inherits Item
27//! \nativetype QQuickOverlay
28 \inqmlmodule QtQuick.Controls
29 \since 5.10
30 \brief A window overlay for popups.
31
32 Overlay provides a layer for popups, ensuring that popups are displayed above
33 other content and that the background is dimmed when a \l {Popup::}{modal} or
34 \l {Popup::dim}{dimmed} popup is visible.
35
36 The overlay is an ordinary Item that covers the entire window. It can be used
37 as a visual parent to position a popup in scene coordinates.
38
39 \include qquickoverlay-popup-parent.qdocinc
40
41 \sa ApplicationWindow
42*/
43
44QList<QQuickPopup *> QQuickOverlayPrivate::stackingOrderPopups() const
45{
46 const QList<QQuickItem *> children = paintOrderChildItems();
47
48 QList<QQuickPopup *> popups;
49 popups.reserve(children.size());
50
51 for (auto it = children.crbegin(), end = children.crend(); it != end; ++it) {
52 QQuickPopup *popup = qobject_cast<QQuickPopup *>((*it)->parent());
53 if (popup)
54 popups += popup;
55 }
56
57 return popups;
58}
59
60QList<QQuickPopup *> QQuickOverlayPrivate::stackingOrderDrawers() const
61{
62 QList<QQuickPopup *> sorted(allDrawers);
63 std::sort(sorted.begin(), sorted.end(), [](const QQuickPopup *one, const QQuickPopup *another) {
64 return one->z() > another->z();
65 });
66 return sorted;
67}
68
69void QQuickOverlayPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &)
70{
71 updateGeometry();
72}
73
74void QQuickOverlayPrivate::itemRotationChanged(QQuickItem *)
75{
76 updateGeometry();
77}
78
79// Wheel and tablet events are not subject to modal popup blocking in delivery agent,
80// so modal popup blocking must be handled explicitly here.
81// Returns true if event is consumed by a modal popup blocking its top-most target.
82#if QT_CONFIG(tabletevent) || QT_CONFIG(wheelevent)
83bool QQuickOverlayPrivate::eatEventIfBlockedByModal(QPointerEvent *event)
84{
85 Q_Q(QQuickOverlay);
86 const QList<QQuickItem *> targetItems = deliveryAgentPrivate()->pointerTargets(
87 window->contentItem(), event, event->point(0), false, false);
88 if (targetItems.isEmpty())
89 return false;
90
91 QQuickItem *const topItem = targetItems.first();
92 QQuickItem *const dimmerItem = q->property("_q_dimmerItem").value<QQuickItem *>();
93 // Find the QQuickPopupItem that contains topItem, if any.
94 QQuickItem *item = qobject_cast<QQuickPopupItem *>(topItem) ? topItem : nullptr;
95
96 if (!item) {
97 item = topItem;
98 while ((item = item->parentItem())) {
99 if (qobject_cast<QQuickPopupItem *>(item))
100 break;
101 }
102 }
103
104#if QT_CONFIG(quick_shadereffect)
105 // topItem may be a QQuickShaderEffectSource created for a popup's drop shadow layer.
106 // It is a direct child of the overlay (not of the popup item), so the parent walk above
107 // misses it. Resolve it to the popup item it renders so the loop below handles it correctly.
108 if (!item && dimmerItem != topItem && q->isAncestorOf(topItem)) {
109 if (auto *shaderEffect = qobject_cast<QQuickShaderEffectSource *>(topItem)) {
110 if (auto *pi = qobject_cast<QQuickPopupItem *>(shaderEffect->sourceItem()))
111 item = pi;
112 }
113 if (!item)
114 return false;
115 }
116#endif
117
118 // Iterate popups front-to-back. Block the event if topItem is not inside a popup
119 // that sits above the first modal popup in stacking order.
120 for (const auto &popup : stackingOrderPopups()) {
121 const QQuickItem *popupItem = popup->popupItem();
122 if (!popupItem)
123 continue;
124 if (popupItem == item) {
125 if (topItem != item && popup->isModal()
126 && !item->contains(item->mapFromScene(event->point(0).scenePosition()))) {
127 // topItem is the popup's shader effect source (drop shadow), not the popup item
128 // itself — the event landed in the shadow area outside the popup's bounds.
129 // Eat it for modal popups to prevent it reaching items behind the shadow.
130 event->accept();
131 return true;
132 }
133 // The event landed inside the popup. Tablet events must be filtered here
134 // because, unlike wheel and mouse events, no standard item
135 // accepts tablet events — so the delivery agent would otherwise continue
136 // past the popup to a TapHandler behind it.
137 // Intentionally not calling event->accept() so Qt synthesizes a mouse event,
138 // which the overlay's mouse press handler delivers to popup content normally.
139#if QT_CONFIG(tabletevent)
140 if (QQuickDeliveryAgentPrivate::isTabletEvent(event))
141 return true;
142#endif
143 break;
144 }
145 // topItem is outside this popup — let the popup decide whether to block.
146 if (popup->overlayEvent(topItem, event))
147 return true;
148 }
149 return false;
150}
151#endif
152
153bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos)
154{
155 Q_Q(QQuickOverlay);
156 if (allDrawers.isEmpty())
157 return false;
158
159 // don't start dragging a drawer if a modal popup overlay is blocking (QTBUG-60602)
160 QQuickItem *item = q->childAt(pos.x(), pos.y());
161 if (item) {
162 const auto popups = stackingOrderPopups();
163 for (QQuickPopup *popup : popups) {
164 QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup);
165 if (p->dimmer == item && popup->isVisible() && popup->isModal())
166 return false;
167 }
168 }
169
170 const QList<QQuickPopup *> drawers = stackingOrderDrawers();
171 for (QQuickPopup *popup : drawers) {
172 QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(popup);
173 Q_ASSERT(drawer);
174 QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer);
175 if (p->startDrag(event)) {
176 setMouseGrabberPopup(drawer);
177 return true;
178 }
179 }
180
181 return false;
182}
183
184bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target)
185{
186 if (target) {
187 if (target->overlayEvent(source, event)) {
188 setMouseGrabberPopup(target);
189 return true;
190 }
191 return false;
192 }
193
194 switch (event->type()) {
195 default: {
196 if (mouseGrabberPopup)
197 break;
198#if QT_CONFIG(quicktemplates2_multitouch)
199 Q_FALLTHROUGH();
200 case QEvent::TouchBegin:
201 case QEvent::TouchUpdate:
202 case QEvent::TouchEnd:
203#endif
204 // allow non-modal popups to close themselves,
205 // and non-dimming modal popups to block the event
206 const auto popups = stackingOrderPopups();
207 for (QQuickPopup *popup : popups) {
208 if (popup->overlayEvent(source, event)) {
209 setMouseGrabberPopup(popup);
210 return true;
211 }
212 }
213 break;
214 }
215 }
216
217 event->ignore();
218 return false;
219}
220
221bool QQuickOverlayPrivate::handleMove(QQuickItem *source, QEvent *event, QQuickPopup *target)
222{
223 if (target)
224 return target->overlayEvent(source, event);
225 return false;
226}
227
228bool QQuickOverlayPrivate::handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target)
229{
230 if (target) {
231 setMouseGrabberPopup(nullptr);
232 if (target->overlayEvent(source, event)) {
233 setMouseGrabberPopup(nullptr);
234 return true;
235 }
236 } else {
237 const auto popups = stackingOrderPopups();
238 for (QQuickPopup *popup : popups) {
239 if (popup->overlayEvent(source, event))
240 return true;
241 }
242 }
243 return false;
244}
245
246bool QQuickOverlayPrivate::handleMouseEvent(QQuickItem *source, QMouseEvent *event, QQuickPopup *target)
247{
248 switch (event->type()) {
249 case QEvent::MouseButtonPress:
250 if (!target && startDrag(event, event->scenePosition()))
251 return true;
252 return handlePress(source, event, target);
253 case QEvent::MouseMove:
254 return handleMove(source, event, target ? target : mouseGrabberPopup.data());
255 case QEvent::MouseButtonRelease:
256 return handleRelease(source, event, target ? target : mouseGrabberPopup.data());
257 default:
258 break;
259 }
260 return false;
261}
262
263bool QQuickOverlayPrivate::handleHoverEvent(QQuickItem *source, QHoverEvent *event, QQuickPopup *target)
264{
265 switch (event->type()) {
266 case QEvent::HoverEnter:
267 case QEvent::HoverMove:
268 case QEvent::HoverLeave:
269 if (target)
270 return target->overlayEvent(source, event);
271 return false;
272 default:
273 Q_UNREACHABLE(); // function must only be called on hover events
274 break;
275 }
276 return false;
277}
278
279#if QT_CONFIG(quicktemplates2_multitouch)
280bool QQuickOverlayPrivate::handleTouchEvent(QQuickItem *source, QTouchEvent *event, QQuickPopup *target)
281{
282 bool handled = false;
283 switch (event->type()) {
284 case QEvent::TouchBegin:
285 case QEvent::TouchUpdate:
286 case QEvent::TouchEnd:
287 for (const QTouchEvent::TouchPoint &point : event->points()) {
288 switch (point.state()) {
289 case QEventPoint::Pressed:
290 if (!target && startDrag(event, point.scenePosition()))
291 handled = true;
292 else
293 handled |= handlePress(source, event, target);
294 break;
295 case QEventPoint::Updated:
296 handled |= handleMove(source, event, target ? target : mouseGrabberPopup.data());
297 break;
298 case QEventPoint::Released:
299 handled |= handleRelease(source, event, target ? target : mouseGrabberPopup.data());
300 break;
301 default:
302 break;
303 }
304 }
305 break;
306
307 default:
308 break;
309 }
310
311 return handled;
312}
313#endif
314
315void QQuickOverlayPrivate::addPopup(QQuickPopup *popup)
316{
317 Q_Q(QQuickOverlay);
318 allPopups += popup;
319 if (QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(popup)) {
320 allDrawers += drawer;
321 q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty());
322 }
323}
324
325void QQuickOverlayPrivate::removePopup(QQuickPopup *popup)
326{
327 Q_Q(QQuickOverlay);
328 allPopups.removeOne(popup);
329 if (allDrawers.removeOne(popup))
330 q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty());
331}
332
333void QQuickOverlayPrivate::setMouseGrabberPopup(QQuickPopup *popup)
334{
335 if (popup && !popup->isVisible())
336 popup = nullptr;
337 mouseGrabberPopup = popup;
338}
339
340void QQuickOverlayPrivate::updateGeometry()
341{
342 Q_Q(QQuickOverlay);
343 if (!window || !window->contentItem())
344 return;
345
346 const QSizeF size = window->contentItem()->size();
347 const QPointF pos(-(size.width() - window->size().width()) / 2,
348 -(size.height() - window->size().height()) / 2);
349
350 q->setSize(size);
351 q->setPosition(pos);
352}
353
354QQuickOverlay::QQuickOverlay(QQuickItem *parent)
355 : QQuickItem(*(new QQuickOverlayPrivate), parent)
356{
357 Q_D(QQuickOverlay);
358 setZ(1000001); // DefaultWindowDecoration+1
359 setAcceptedMouseButtons(Qt::AllButtons);
360#if QT_CONFIG(quicktemplates2_multitouch)
361 setAcceptTouchEvents(true);
362#endif
363 setFiltersChildMouseEvents(true);
364 setVisible(false);
365
366 if (parent) {
367 d->updateGeometry();
368 QQuickItemPrivate::get(parent)->addItemChangeListener(d, QQuickItemPrivate::Geometry);
369 if (QQuickWindow *window = parent->window()) {
370 window->installEventFilter(this);
371 if (QQuickItem *contentItem = window->contentItem())
372 QQuickItemPrivate::get(contentItem)->addItemChangeListener(d, QQuickItemPrivate::Rotation);
373 }
374 }
375}
376
377QQuickOverlay::~QQuickOverlay()
378{
379 Q_D(QQuickOverlay);
380 if (QQuickItem *parent = parentItem()) {
381 QQuickItemPrivate::get(parent)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
382 if (QQuickWindow *window = parent->window()) {
383 if (QQuickItem *contentItem = window->contentItem())
384 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Rotation);
385 }
386 }
387}
388
389QQmlComponent *QQuickOverlay::modal() const
390{
391 Q_D(const QQuickOverlay);
392 return d->modal;
393}
394
395void QQuickOverlay::setModal(QQmlComponent *modal)
396{
397 Q_D(QQuickOverlay);
398 if (d->modal == modal)
399 return;
400
401 d->modal = modal;
402 emit modalChanged();
403}
404
405QQmlComponent *QQuickOverlay::modeless() const
406{
407 Q_D(const QQuickOverlay);
408 return d->modeless;
409}
410
411void QQuickOverlay::setModeless(QQmlComponent *modeless)
412{
413 Q_D(QQuickOverlay);
414 if (d->modeless == modeless)
415 return;
416
417 d->modeless = modeless;
418 emit modelessChanged();
419}
420
421QQuickOverlay *QQuickOverlay::overlay(QQuickWindow *window, QQuickItem *parent)
422{
423 if (!window)
424 return nullptr;
425
426 const char *name = "_q_QQuickOverlay";
427
428 if (QQuickItemPrivate::customOverlayRequested) {
429 while (parent) {
430 if (QQuickItemPrivate::get(parent)->customOverlay) {
431 QQuickOverlay *overlay = parent->property(name).value<QQuickOverlay *>();
432 if (!overlay) {
433 overlay = new QQuickOverlay(parent);
434 parent->setProperty(name, QVariant::fromValue(overlay));
435 }
436 return overlay;
437 }
438 parent = parent->parentItem();
439 }
440 }
441 QQuickOverlay *overlay = window->property(name).value<QQuickOverlay *>();
442 if (!overlay) {
443 QQuickItem *content = window->contentItem();
444 // Do not re-create the overlay if the window is being destroyed
445 // and thus, its content item no longer has a window associated.
446 if (content && content->window()) {
447 overlay = new QQuickOverlay(window->contentItem());
448 window->setProperty(name, QVariant::fromValue(overlay));
449 }
450 }
451 return overlay;
452}
453
454QQuickOverlayAttached *QQuickOverlay::qmlAttachedProperties(QObject *object)
455{
456 return new QQuickOverlayAttached(object);
457}
458
459void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data)
460{
461 Q_D(QQuickOverlay);
462 QQuickItem::itemChange(change, data);
463
464 if (change == ItemChildAddedChange || change == ItemChildRemovedChange) {
465 setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty());
466 if (data.item->parent() == d->mouseGrabberPopup)
467 d->setMouseGrabberPopup(nullptr);
468 }
469}
470
471void QQuickOverlay::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
472{
473 Q_D(QQuickOverlay);
474 QQuickItem::geometryChange(newGeometry, oldGeometry);
475 for (QQuickPopup *popup : std::as_const(d->allPopups))
476 QQuickPopupPrivate::get(popup)->resizeDimmer();
477}
478
479void QQuickOverlay::mousePressEvent(QMouseEvent *event)
480{
481 Q_D(QQuickOverlay);
482 d->handleMouseEvent(this, event);
483}
484
485void QQuickOverlay::mouseMoveEvent(QMouseEvent *event)
486{
487 Q_D(QQuickOverlay);
488 d->handleMouseEvent(this, event);
489}
490
491void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event)
492{
493 Q_D(QQuickOverlay);
494 d->handleMouseEvent(this, event);
495}
496
497#if QT_CONFIG(quicktemplates2_multitouch)
498void QQuickOverlay::touchEvent(QTouchEvent *event)
499{
500 Q_D(QQuickOverlay);
501 d->handleTouchEvent(this, event);
502}
503#endif
504
505void QQuickOverlay::handleWheelAndDnDEvents(QEvent *event)
506{
507 Q_D(QQuickOverlay);
508 if (d->mouseGrabberPopup) {
509 d->mouseGrabberPopup->overlayEvent(this, event);
510 return;
511 } else {
512 const auto popups = d->stackingOrderPopups();
513 for (QQuickPopup *popup : popups) {
514 if (popup->overlayEvent(this, event))
515 return;
516 }
517 }
518 event->ignore();
519}
520
521#if QT_CONFIG(wheelevent)
522void QQuickOverlay::wheelEvent(QWheelEvent *event)
523{
524 handleWheelAndDnDEvents(event);
525}
526#endif
527
528#if QT_CONFIG(quick_draganddrop)
529void QQuickOverlay::dragEnterEvent(QDragEnterEvent *event)
530{
531 handleWheelAndDnDEvents(event);
532}
533
534void QQuickOverlay::dragMoveEvent(QDragMoveEvent *event)
535{
536 handleWheelAndDnDEvents(event);
537}
538
539void QQuickOverlay::dragLeaveEvent(QDragLeaveEvent *event)
540{
541 handleWheelAndDnDEvents(event);
542}
543
544void QQuickOverlay::dropEvent(QDropEvent *event)
545{
546 handleWheelAndDnDEvents(event);
547}
548#endif
549
550/*!
551 \internal
552
553 When clicking inside a scene with a single active popup, a few things can happen.
554 The click can be inside the popup item, in which case, we stop filtering to allow
555 normal event handling. Or the click can be outside the popup, in which case,
556 it will normally be inside the overlay, or the popup's dimmer item.
557
558 When dealing with nested popups, the second popup's dimmer (or popupItem if the dimmer is absent)
559 will become an exclusive grabber to the pointerEvent, during childMouseEventFilter.
560 (sometimes the overlay becomes the exclusive grabber instead, why?).
561*/
562bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event)
563{
564 Q_D(QQuickOverlay);
565 const auto popups = d->stackingOrderPopups();
566 for (QQuickPopup *popup : popups) {
567 QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup);
568
569 // Stop filtering overlay events when reaching a popup item or an item
570 // that is inside the popup. Let the popup content handle its events.
571 if (item == p->popupItem || p->popupItem->isAncestorOf(item))
572 break;
573
574 // Let the popup try closing itself when pressing or releasing over its
575 // background dimming OR over another popup underneath, in case the popup
576 // does not have background dimming.
577 if (item == p->dimmer || !p->popupItem->isAncestorOf(item)) {
578 bool handled = false;
579 switch (event->type()) {
580#if QT_CONFIG(quicktemplates2_multitouch)
581 case QEvent::TouchBegin:
582 case QEvent::TouchUpdate:
583 case QEvent::TouchEnd:
584 handled = d->handleTouchEvent(item, static_cast<QTouchEvent *>(event), popup);
585 break;
586#endif
587 case QEvent::HoverEnter:
588 case QEvent::HoverMove:
589 case QEvent::HoverLeave:
590 // If the control item has already been hovered, allow the hover leave event
591 // to be processed by the same item for resetting its internal hovered state
592 // instead of filtering it here.
593 if (auto *control = qobject_cast<QQuickControl *>(item)) {
594 if (control->isHovered() && event->type() == QEvent::HoverLeave)
595 return false;
596 }
597 handled = d->handleHoverEvent(item, static_cast<QHoverEvent *>(event), popup);
598 break;
599
600 case QEvent::MouseButtonPress:
601 case QEvent::MouseMove:
602 case QEvent::MouseButtonRelease:
603 handled = d->handleMouseEvent(item, static_cast<QMouseEvent *>(event), popup);
604 break;
605
606 default:
607 break;
608 }
609 if (handled)
610 return true;
611 }
612 }
613 return false;
614}
615
616/*!
617 \internal
618
619 The overlay installs itself as an event filter on the window it belongs to.
620 It will filter Touch, Mouse (press and release) and Wheel related events.
621
622 Touch and MousePress events will be passed to the delivery agent for normal event propagation,
623 where they will be filtered by the overlay again in QQuickOverlay::childMouseEventFilter.
624 All opened popups will then be iterated, to check if the event should close the popup or not.
625 Also modality is checked to determine the return type, which will cause the delivery agent to
626 continue normal propagation in this second childMouseEventFilter filtering step.
627
628 The reason for installing the eventFilter is to allow non-modal popups with CloseOnReleaseOutside
629 as the closing policy to close when clicking the overlay. The delivery agent won't send the
630 release event to the overlay when clicking it directly, only the press event.
631*/
632bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
633{
634 Q_D(QQuickOverlay);
635 if (!isVisible() || object != d->window)
636 return false;
637
638#if QT_CONFIG(wheelevent) || QT_CONFIG(quick_draganddrop)
639 auto targetItemsForEvent = [&](QEvent *){
640#if QT_CONFIG(wheelevent)
641 if (event->type() == QEvent::Wheel) {
642 QWheelEvent *we = static_cast<QWheelEvent *>(event);
643 return d->deliveryAgentPrivate()->pointerTargets(
644 d->window->contentItem(), we, we->point(0), false, false);
645 }
646#endif
647#if QT_CONFIG(quick_draganddrop)
648 bool isDnDEvent = false;
649 switch (event->type()) {
650 case QEvent::DragEnter:
651 case QEvent::DragMove:
652 case QEvent::Drop:
653 isDnDEvent = true;
654 break;
655 default:
656 break;
657 }
658 if (isDnDEvent) {
659 QDropEvent *de = static_cast<QDropEvent *>(event);
660 auto position = mapFromScene(de->position());
661 return d->deliveryAgentPrivate()->eventTargets(
662 d->window->contentItem(), de, -1, position, de->position(),
663 [](QQuickItem *item, const QEvent *) -> std::optional<bool> {
664 // Only consider actual drop targets; still recurse into
665 // non-accepting items so their children can be found.
666 if (!item->flags().testFlag(QQuickItem::ItemAcceptsDrops))
667 return false;
668 return std::nullopt; // use default containment check
669 });
670 }
671#endif
672 // Not needed so far
673 Q_UNREACHABLE_RETURN(QList<QQuickItem *> {});
674 };
675#endif
676
677 switch (event->type()) {
678#if QT_CONFIG(quicktemplates2_multitouch)
679 case QEvent::TouchBegin:
680 case QEvent::TouchUpdate:
681 case QEvent::TouchEnd:
682 if (static_cast<QTouchEvent *>(event)->touchPointStates() & QEventPoint::Pressed)
683 emit pressed();
684 if (static_cast<QTouchEvent *>(event)->touchPointStates() & QEventPoint::Released)
685 emit released();
686
687 // allow non-modal popups to close on touch release outside
688 if (!d->mouseGrabberPopup) {
689 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
690 for (const QTouchEvent::TouchPoint &point : touchEvent->points()) {
691 if (point.state() == QEventPoint::Released) {
692 QQuickDeliveryAgentPrivate::translateTouchEvent(touchEvent);
693 if (d->handleRelease(d->window->contentItem(), event, nullptr))
694 break;
695 }
696 }
697 }
698
699 // setup currentEventDeliveryAgent like in QQuickDeliveryAgent::event
700 QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = d->deliveryAgent();
701 d->deliveryAgentPrivate()->handleTouchEvent(static_cast<QTouchEvent *>(event));
702 QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = nullptr;
703
704 // If a touch event hasn't been accepted after being delivered, there
705 // were no items interested in touch events at any of the touch points.
706 // Make sure to accept the touch event in order to receive the consequent
707 // touch events, to be able to close non-modal popups on release outside.
708 event->accept();
709 // Since we eat the event, QQuickWindow::event never sees it to clean up the
710 // grabber states. So we have to do so explicitly.
711 d->deliveryAgentPrivate()->clearGrabbers(static_cast<QPointerEvent *>(event));
712 return true;
713#endif
714
715 case QEvent::MouseButtonPress: {
716 auto *mouseEvent = static_cast<QMouseEvent *>(event);
717 // Don't filter right mouse button clicks, as it prevents ContextMenu from
718 // receiving QContextMenuEvents as long as e.g. a Drawer exists, even if it's not visible.
719 // This does not prevent popups from being closed with the right mouse button,
720 // as mousePressEvent takes care of that.
721 if (mouseEvent->button() == Qt::RightButton)
722 break;
723
724#if QT_CONFIG(quicktemplates2_multitouch)
725 // do not emit pressed() twice when mouse events have been synthesized from touch events
726 if (mouseEvent->source() == Qt::MouseEventNotSynthesized)
727#endif
728 emit pressed();
729
730 // setup currentEventDeliveryAgent like in QQuickDeliveryAgent::event
731 QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = d->deliveryAgent();
732 d->deliveryAgentPrivate()->handleMouseEvent(mouseEvent);
733 QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = nullptr;
734
735 // If a mouse event hasn't been accepted after being delivered, there
736 // was no item interested in mouse events at the mouse point. Make sure
737 // to accept the mouse event in order to receive the consequent mouse
738 // events, to be able to close non-modal popups on release outside.
739 event->accept();
740 return true;
741 }
742 case QEvent::MouseButtonRelease: {
743 auto *mouseEvent = static_cast<QMouseEvent *>(event);
744 if (mouseEvent->button() == Qt::RightButton)
745 break;
746
747#if QT_CONFIG(quicktemplates2_multitouch)
748 // do not emit released() twice when mouse events have been synthesized from touch events
749 if (mouseEvent->source() == Qt::MouseEventNotSynthesized)
750#endif
751 emit released();
752
753 // allow non-modal popups to close on mouse release outside
754 if (!d->mouseGrabberPopup)
755 d->handleRelease(d->window->contentItem(), event, nullptr);
756 break;
757 }
758#if QT_CONFIG(quick_draganddrop)
759 case QEvent::DragEnter:
760 case QEvent::DragMove:
761 case QEvent::Drop: {
762 // Find which item the drag is targeting and block if a modal popup is in front.
763 const QList<QQuickItem *> targetItems = targetItemsForEvent(event);
764 if (targetItems.isEmpty())
765 break;
766 QQuickItem *const dimmerItem = property("_q_dimmerItem").value<QQuickItem *>();
767 QQuickItem *const topItem = targetItems.first();
768 QQuickItem *item = qobject_cast<QQuickPopupItem *>(topItem) ? topItem : nullptr;
769 if (!item) {
770 item = topItem;
771 while ((item = item->parentItem())) {
772 if (qobject_cast<QQuickPopupItem *>(item))
773 break;
774 }
775 }
776 if (!item && dimmerItem != topItem && isAncestorOf(topItem))
777 break;
778 for (const auto &popup : d->stackingOrderPopups()) {
779 const QQuickItem *popupItem = popup->popupItem();
780 if (!popupItem)
781 continue;
782 if (popupItem == item)
783 break;
784 if (popup->overlayEvent(topItem, event))
785 return true;
786 }
787 break;
788 }
789#endif
790#if QT_CONFIG(wheelevent)
791 case QEvent::Wheel:
792 return d->eatEventIfBlockedByModal(static_cast<QWheelEvent *>(event));
793#endif
794#if QT_CONFIG(tabletevent)
795 case QEvent::TabletMove:
796 case QEvent::TabletPress:
797 case QEvent::TabletRelease:
798 return d->eatEventIfBlockedByModal(static_cast<QTabletEvent *>(event));
799#endif
800 default:
801 break;
802 }
803
804 return false;
805}
806
808{
809public:
810 Q_DECLARE_PUBLIC(QQuickOverlayAttached)
811
813 void setWindowAndParent(QQuickWindow *newWindow, QQuickItem *parent);
814
816 QQmlComponent *modal = nullptr;
817 QQmlComponent *modeless = nullptr;
819};
820
821void QQuickOverlayAttachedPrivate::setWindow(QQuickWindow *newWindow)
822{
823 setWindowAndParent(newWindow, parentItem);
824}
825
826void QQuickOverlayAttachedPrivate::setWindowAndParent(QQuickWindow *newWindow, QQuickItem *parent)
827{
828 Q_Q(QQuickOverlayAttached);
829 if (window == newWindow)
830 return;
831
832 if (QQuickOverlay *oldOverlay = QQuickOverlay::overlay(window, parent)) {
833 QObject::disconnect(oldOverlay, &QQuickOverlay::pressed, q, &QQuickOverlayAttached::pressed);
834 QObject::disconnect(oldOverlay, &QQuickOverlay::released, q, &QQuickOverlayAttached::released);
835 }
836
837 if (QQuickOverlay *newOverlay = QQuickOverlay::overlay(newWindow, parent)) {
838 QObject::connect(newOverlay, &QQuickOverlay::pressed, q, &QQuickOverlayAttached::pressed);
839 QObject::connect(newOverlay, &QQuickOverlay::released, q, &QQuickOverlayAttached::released);
840 }
841
842 window = newWindow;
843 if (parent)
844 parentItem = parent;
845 emit q->overlayChanged();
846}
847
848/*!
849 \qmlattachedsignal QtQuick.Controls::Overlay::pressed()
850
851 This attached signal is emitted when the overlay is pressed by the user while
852 a popup is visible.
853
854 The signal can be attached to any item, popup, or window. When attached to an
855 item or a popup, the signal is only emitted if the item or popup is in a window.
856
857 \include qquickoverlay-pressed-released.qdocinc
858*/
859
860/*!
861 \qmlattachedsignal QtQuick.Controls::Overlay::released()
862
863 This attached signal is emitted when the overlay is released by the user while
864 a popup is visible.
865
866 The signal can be attached to any item, popup, or window. When attached to an
867 item or a popup, the signal is only emitted if the item or popup is in a window.
868
869 \include qquickoverlay-pressed-released.qdocinc
870*/
871
872QQuickOverlayAttached::QQuickOverlayAttached(QObject *parent)
873 : QObject(*(new QQuickOverlayAttachedPrivate), parent)
874{
875 Q_D(QQuickOverlayAttached);
876 if (QQuickItem *item = qobject_cast<QQuickItem *>(parent)) {
877 d->setWindowAndParent(item->window(), item);
878 QObjectPrivate::connect(item, &QQuickItem::windowChanged, d, &QQuickOverlayAttachedPrivate::setWindow);
879 } else if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent)) {
880 d->setWindowAndParent(popup->window(), popup->parentItem());
881 QObjectPrivate::connect(popup, &QQuickPopup::windowChanged, d, &QQuickOverlayAttachedPrivate::setWindow);
882 } else {
883 d->setWindow(qobject_cast<QQuickWindow *>(parent));
884 }
885}
886
887/*!
888 \qmlattachedproperty Overlay QtQuick.Controls::Overlay::overlay
889 \readonly
890
891 This attached property holds the window overlay item.
892
893 The property can be attached to any item, popup, or window. When attached to an
894 item or a popup, the value is \c null if the item or popup is not in a window.
895*/
896QQuickOverlay *QQuickOverlayAttached::overlay() const
897{
898 Q_D(const QQuickOverlayAttached);
899 return QQuickOverlay::overlay(d->window, d->parentItem);
900}
901
902/*!
903 \qmlattachedproperty Component QtQuick.Controls::Overlay::modal
904
905 This attached property holds a component to use as a visual item that implements
906 background dimming for modal popups. It is created for and stacked below visible
907 modal popups.
908
909 The property can be attached to any popup.
910
911 For example, to change the color of the background dimming for a modal
912 popup, the following code can be used:
913
914 \snippet qtquickcontrols-overlay-modal.qml 1
915
916 \sa Popup::modal
917*/
918QQmlComponent *QQuickOverlayAttached::modal() const
919{
920 Q_D(const QQuickOverlayAttached);
921 return d->modal;
922}
923
924void QQuickOverlayAttached::setModal(QQmlComponent *modal)
925{
926 Q_D(QQuickOverlayAttached);
927 if (d->modal == modal)
928 return;
929
930 d->modal = modal;
931 emit modalChanged();
932}
933
934/*!
935 \qmlattachedproperty Component QtQuick.Controls::Overlay::modeless
936
937 This attached property holds a component to use as a visual item that implements
938 background dimming for modeless popups. It is created for and stacked below visible
939 dimming popups.
940
941 The property can be attached to any popup.
942
943 For example, to change the color of the background dimming for a modeless
944 popup, the following code can be used:
945
946 \snippet qtquickcontrols-overlay-modeless.qml 1
947
948 \sa Popup::dim
949*/
950QQmlComponent *QQuickOverlayAttached::modeless() const
951{
952 Q_D(const QQuickOverlayAttached);
953 return d->modeless;
954}
955
956void QQuickOverlayAttached::setModeless(QQmlComponent *modeless)
957{
958 Q_D(QQuickOverlayAttached);
959 if (d->modeless == modeless)
960 return;
961
962 d->modeless = modeless;
963 emit modelessChanged();
964}
965
966QT_END_NAMESPACE
967
968#include "moc_qquickoverlay_p.cpp"
void setWindowAndParent(QQuickWindow *newWindow, QQuickItem *parent)