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