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 | |
4 | #include "qquickcontrol_p_p.h" |
5 | #include "qquickoverlay_p.h" |
6 | #include "qquickoverlay_p_p.h" |
7 | #include "qquickpopupitem_p_p.h" |
8 | #include "qquickpopup_p_p.h" |
9 | #include "qquickdrawer_p.h" |
10 | #include "qquickdrawer_p_p.h" |
11 | #include "qquickapplicationwindow_p.h" |
12 | #include <QtQml/qqmlinfo.h> |
13 | #include <QtQml/qqmlproperty.h> |
14 | #include <QtQml/qqmlcomponent.h> |
15 | #include <algorithm> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | /*! |
20 | \qmltype Overlay |
21 | \inherits Item |
22 | //! \instantiates QQuickOverlay |
23 | \inqmlmodule QtQuick.Controls |
24 | \since 5.10 |
25 | \brief A window overlay for popups. |
26 | |
27 | Overlay provides a layer for popups, ensuring that popups are displayed above |
28 | other content and that the background is dimmed when a \l {Popup::}{modal} or |
29 | \l {Popup::dim}{dimmed} popup is visible. |
30 | |
31 | The overlay is an ordinary Item that covers the entire window. It can be used |
32 | as a visual parent to position a popup in scene coordinates. |
33 | |
34 | \include qquickoverlay-popup-parent.qdocinc |
35 | |
36 | \sa ApplicationWindow |
37 | */ |
38 | |
39 | QList<QQuickPopup *> QQuickOverlayPrivate::() const |
40 | { |
41 | const QList<QQuickItem *> children = paintOrderChildItems(); |
42 | |
43 | QList<QQuickPopup *> ; |
44 | popups.reserve(size: children.size()); |
45 | |
46 | for (auto it = children.crbegin(), end = children.crend(); it != end; ++it) { |
47 | QQuickPopup * = qobject_cast<QQuickPopup *>(object: (*it)->parent()); |
48 | if (popup) |
49 | popups += popup; |
50 | } |
51 | |
52 | return popups; |
53 | } |
54 | |
55 | QList<QQuickPopup *> QQuickOverlayPrivate::stackingOrderDrawers() const |
56 | { |
57 | QList<QQuickPopup *> sorted(allDrawers); |
58 | std::sort(first: sorted.begin(), last: sorted.end(), comp: [](const QQuickPopup *one, const QQuickPopup *another) { |
59 | return one->z() > another->z(); |
60 | }); |
61 | return sorted; |
62 | } |
63 | |
64 | void QQuickOverlayPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) |
65 | { |
66 | updateGeometry(); |
67 | } |
68 | |
69 | bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos) |
70 | { |
71 | Q_Q(QQuickOverlay); |
72 | if (allDrawers.isEmpty()) |
73 | return false; |
74 | |
75 | // don't start dragging a drawer if a modal popup overlay is blocking (QTBUG-60602) |
76 | QQuickItem *item = q->childAt(x: pos.x(), y: pos.y()); |
77 | if (item) { |
78 | const auto = stackingOrderPopups(); |
79 | for (QQuickPopup * : popups) { |
80 | QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); |
81 | if (p->dimmer == item && popup->isVisible() && popup->isModal()) |
82 | return false; |
83 | } |
84 | } |
85 | |
86 | const QList<QQuickPopup *> drawers = stackingOrderDrawers(); |
87 | for (QQuickPopup * : drawers) { |
88 | QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(object: popup); |
89 | Q_ASSERT(drawer); |
90 | QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); |
91 | if (p->startDrag(event)) { |
92 | setMouseGrabberPopup(drawer); |
93 | return true; |
94 | } |
95 | } |
96 | |
97 | return false; |
98 | } |
99 | |
100 | bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target) |
101 | { |
102 | if (target) { |
103 | if (target->overlayEvent(item: source, event)) { |
104 | setMouseGrabberPopup(target); |
105 | return true; |
106 | } |
107 | return false; |
108 | } |
109 | |
110 | switch (event->type()) { |
111 | default: { |
112 | if (mouseGrabberPopup) |
113 | break; |
114 | #if QT_CONFIG(quicktemplates2_multitouch) |
115 | Q_FALLTHROUGH(); |
116 | case QEvent::TouchBegin: |
117 | case QEvent::TouchUpdate: |
118 | case QEvent::TouchEnd: |
119 | #endif |
120 | // allow non-modal popups to close themselves, |
121 | // and non-dimming modal popups to block the event |
122 | const auto = stackingOrderPopups(); |
123 | for (QQuickPopup * : popups) { |
124 | if (popup->overlayEvent(item: source, event)) { |
125 | setMouseGrabberPopup(popup); |
126 | return true; |
127 | } |
128 | } |
129 | break; |
130 | } |
131 | } |
132 | |
133 | event->ignore(); |
134 | return false; |
135 | } |
136 | |
137 | bool QQuickOverlayPrivate::handleMove(QQuickItem *source, QEvent *event, QQuickPopup *target) |
138 | { |
139 | if (target) |
140 | return target->overlayEvent(item: source, event); |
141 | return false; |
142 | } |
143 | |
144 | bool QQuickOverlayPrivate::handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target) |
145 | { |
146 | if (target) { |
147 | setMouseGrabberPopup(nullptr); |
148 | if (target->overlayEvent(item: source, event)) { |
149 | setMouseGrabberPopup(nullptr); |
150 | return true; |
151 | } |
152 | } else { |
153 | const auto = stackingOrderPopups(); |
154 | for (QQuickPopup * : popups) { |
155 | if (popup->overlayEvent(item: source, event)) |
156 | return true; |
157 | } |
158 | } |
159 | return false; |
160 | } |
161 | |
162 | bool QQuickOverlayPrivate::handleMouseEvent(QQuickItem *source, QMouseEvent *event, QQuickPopup *target) |
163 | { |
164 | switch (event->type()) { |
165 | case QEvent::MouseButtonPress: |
166 | if (!target && startDrag(event, pos: event->scenePosition())) |
167 | return true; |
168 | return handlePress(source, event, target); |
169 | case QEvent::MouseMove: |
170 | return handleMove(source, event, target: target ? target : mouseGrabberPopup.data()); |
171 | case QEvent::MouseButtonRelease: |
172 | return handleRelease(source, event, target: target ? target : mouseGrabberPopup.data()); |
173 | default: |
174 | break; |
175 | } |
176 | return false; |
177 | } |
178 | |
179 | bool QQuickOverlayPrivate::handleHoverEvent(QQuickItem *source, QHoverEvent *event, QQuickPopup *target) |
180 | { |
181 | switch (event->type()) { |
182 | case QEvent::HoverEnter: |
183 | case QEvent::HoverMove: |
184 | case QEvent::HoverLeave: |
185 | if (target) |
186 | return target->overlayEvent(item: source, event); |
187 | return false; |
188 | default: |
189 | Q_UNREACHABLE(); // function must only be called on hover events |
190 | break; |
191 | } |
192 | return false; |
193 | } |
194 | |
195 | #if QT_CONFIG(quicktemplates2_multitouch) |
196 | bool QQuickOverlayPrivate::handleTouchEvent(QQuickItem *source, QTouchEvent *event, QQuickPopup *target) |
197 | { |
198 | bool handled = false; |
199 | switch (event->type()) { |
200 | case QEvent::TouchBegin: |
201 | case QEvent::TouchUpdate: |
202 | case QEvent::TouchEnd: |
203 | for (const QTouchEvent::TouchPoint &point : event->points()) { |
204 | switch (point.state()) { |
205 | case QEventPoint::Pressed: |
206 | if (!target && startDrag(event, pos: point.scenePosition())) |
207 | handled = true; |
208 | else |
209 | handled |= handlePress(source, event, target); |
210 | break; |
211 | case QEventPoint::Updated: |
212 | handled |= handleMove(source, event, target: target ? target : mouseGrabberPopup.data()); |
213 | break; |
214 | case QEventPoint::Released: |
215 | handled |= handleRelease(source, event, target: target ? target : mouseGrabberPopup.data()); |
216 | break; |
217 | default: |
218 | break; |
219 | } |
220 | } |
221 | break; |
222 | |
223 | default: |
224 | break; |
225 | } |
226 | |
227 | return handled; |
228 | } |
229 | #endif |
230 | |
231 | void QQuickOverlayPrivate::(QQuickPopup *) |
232 | { |
233 | Q_Q(QQuickOverlay); |
234 | allPopups += popup; |
235 | if (QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(object: popup)) { |
236 | allDrawers += drawer; |
237 | q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty()); |
238 | } |
239 | } |
240 | |
241 | void QQuickOverlayPrivate::(QQuickPopup *) |
242 | { |
243 | Q_Q(QQuickOverlay); |
244 | allPopups.removeOne(t: popup); |
245 | if (allDrawers.removeOne(t: popup)) |
246 | q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty()); |
247 | } |
248 | |
249 | void QQuickOverlayPrivate::(QQuickPopup *) |
250 | { |
251 | if (popup && !popup->isVisible()) |
252 | popup = nullptr; |
253 | mouseGrabberPopup = popup; |
254 | } |
255 | |
256 | void QQuickOverlayPrivate::updateGeometry() |
257 | { |
258 | Q_Q(QQuickOverlay); |
259 | if (!window) |
260 | return; |
261 | |
262 | QPointF pos; |
263 | QSizeF size = window->size(); |
264 | qreal rotation = 0; |
265 | |
266 | switch (window->contentOrientation()) { |
267 | case Qt::PrimaryOrientation: |
268 | case Qt::PortraitOrientation: |
269 | size = window->size(); |
270 | break; |
271 | case Qt::LandscapeOrientation: |
272 | rotation = 90; |
273 | pos = QPointF((size.width() - size.height()) / 2, -(size.width() - size.height()) / 2); |
274 | size.transpose(); |
275 | break; |
276 | case Qt::InvertedPortraitOrientation: |
277 | rotation = 180; |
278 | break; |
279 | case Qt::InvertedLandscapeOrientation: |
280 | rotation = 270; |
281 | pos = QPointF((size.width() - size.height()) / 2, -(size.width() - size.height()) / 2); |
282 | size.transpose(); |
283 | break; |
284 | default: |
285 | break; |
286 | } |
287 | |
288 | q->setSize(size); |
289 | q->setPosition(pos); |
290 | q->setRotation(rotation); |
291 | } |
292 | |
293 | QQuickOverlay::QQuickOverlay(QQuickItem *parent) |
294 | : QQuickItem(*(new QQuickOverlayPrivate), parent) |
295 | { |
296 | Q_D(QQuickOverlay); |
297 | setZ(1000001); // DefaultWindowDecoration+1 |
298 | setAcceptedMouseButtons(Qt::AllButtons); |
299 | #if QT_CONFIG(quicktemplates2_multitouch) |
300 | setAcceptTouchEvents(true); |
301 | #endif |
302 | setFiltersChildMouseEvents(true); |
303 | setVisible(false); |
304 | |
305 | if (parent) { |
306 | d->updateGeometry(); |
307 | QQuickItemPrivate::get(item: parent)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Geometry); |
308 | if (QQuickWindow *window = parent->window()) { |
309 | window->installEventFilter(filterObj: this); |
310 | QObjectPrivate::connect(sender: window, signal: &QWindow::contentOrientationChanged, receiverPrivate: d, slot: &QQuickOverlayPrivate::updateGeometry); |
311 | } |
312 | } |
313 | } |
314 | |
315 | QQuickOverlay::~QQuickOverlay() |
316 | { |
317 | Q_D(QQuickOverlay); |
318 | if (QQuickItem *parent = parentItem()) |
319 | QQuickItemPrivate::get(item: parent)->removeItemChangeListener(d, types: QQuickItemPrivate::Geometry); |
320 | } |
321 | |
322 | QQmlComponent *QQuickOverlay::modal() const |
323 | { |
324 | Q_D(const QQuickOverlay); |
325 | return d->modal; |
326 | } |
327 | |
328 | void QQuickOverlay::setModal(QQmlComponent *modal) |
329 | { |
330 | Q_D(QQuickOverlay); |
331 | if (d->modal == modal) |
332 | return; |
333 | |
334 | d->modal = modal; |
335 | emit modalChanged(); |
336 | } |
337 | |
338 | QQmlComponent *QQuickOverlay::modeless() const |
339 | { |
340 | Q_D(const QQuickOverlay); |
341 | return d->modeless; |
342 | } |
343 | |
344 | void QQuickOverlay::setModeless(QQmlComponent *modeless) |
345 | { |
346 | Q_D(QQuickOverlay); |
347 | if (d->modeless == modeless) |
348 | return; |
349 | |
350 | d->modeless = modeless; |
351 | emit modelessChanged(); |
352 | } |
353 | |
354 | QQuickOverlay *QQuickOverlay::overlay(QQuickWindow *window) |
355 | { |
356 | if (!window) |
357 | return nullptr; |
358 | |
359 | const char *name = "_q_QQuickOverlay" ; |
360 | QQuickOverlay *overlay = window->property(name).value<QQuickOverlay *>(); |
361 | if (!overlay) { |
362 | QQuickItem *content = window->contentItem(); |
363 | // Do not re-create the overlay if the window is being destroyed |
364 | // and thus, its content item no longer has a window associated. |
365 | if (content && content->window()) { |
366 | overlay = new QQuickOverlay(window->contentItem()); |
367 | window->setProperty(name, value: QVariant::fromValue(value: overlay)); |
368 | } |
369 | } |
370 | return overlay; |
371 | } |
372 | |
373 | QQuickOverlayAttached *QQuickOverlay::qmlAttachedProperties(QObject *object) |
374 | { |
375 | return new QQuickOverlayAttached(object); |
376 | } |
377 | |
378 | void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) |
379 | { |
380 | Q_D(QQuickOverlay); |
381 | QQuickItem::itemChange(change, data); |
382 | |
383 | if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { |
384 | setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty()); |
385 | if (data.item->parent() == d->mouseGrabberPopup) |
386 | d->setMouseGrabberPopup(nullptr); |
387 | } |
388 | } |
389 | |
390 | void QQuickOverlay::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
391 | { |
392 | Q_D(QQuickOverlay); |
393 | QQuickItem::geometryChange(newGeometry, oldGeometry); |
394 | for (QQuickPopup * : std::as_const(t&: d->allPopups)) |
395 | QQuickPopupPrivate::get(popup)->resizeOverlay(); |
396 | } |
397 | |
398 | void QQuickOverlay::mousePressEvent(QMouseEvent *event) |
399 | { |
400 | Q_D(QQuickOverlay); |
401 | d->handleMouseEvent(source: this, event); |
402 | } |
403 | |
404 | void QQuickOverlay::mouseMoveEvent(QMouseEvent *event) |
405 | { |
406 | Q_D(QQuickOverlay); |
407 | d->handleMouseEvent(source: this, event); |
408 | } |
409 | |
410 | void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event) |
411 | { |
412 | Q_D(QQuickOverlay); |
413 | d->handleMouseEvent(source: this, event); |
414 | } |
415 | |
416 | #if QT_CONFIG(quicktemplates2_multitouch) |
417 | void QQuickOverlay::touchEvent(QTouchEvent *event) |
418 | { |
419 | Q_D(QQuickOverlay); |
420 | d->handleTouchEvent(source: this, event); |
421 | } |
422 | #endif |
423 | |
424 | #if QT_CONFIG(wheelevent) |
425 | void QQuickOverlay::wheelEvent(QWheelEvent *event) |
426 | { |
427 | Q_D(QQuickOverlay); |
428 | if (d->mouseGrabberPopup) { |
429 | d->mouseGrabberPopup->overlayEvent(item: this, event); |
430 | return; |
431 | } else { |
432 | const auto = d->stackingOrderPopups(); |
433 | for (QQuickPopup * : popups) { |
434 | if (popup->overlayEvent(item: this, event)) |
435 | return; |
436 | } |
437 | } |
438 | event->ignore(); |
439 | } |
440 | #endif |
441 | |
442 | bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) |
443 | { |
444 | Q_D(QQuickOverlay); |
445 | const auto = d->stackingOrderPopups(); |
446 | for (QQuickPopup * : popups) { |
447 | QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); |
448 | |
449 | // Stop filtering overlay events when reaching a popup item or an item |
450 | // that is inside the popup. Let the popup content handle its events. |
451 | if (item == p->popupItem || p->popupItem->isAncestorOf(child: item)) |
452 | break; |
453 | |
454 | // Let the popup try closing itself when pressing or releasing over its |
455 | // background dimming OR over another popup underneath, in case the popup |
456 | // does not have background dimming. |
457 | if (item == p->dimmer || !p->popupItem->isAncestorOf(child: item)) { |
458 | bool handled = false; |
459 | switch (event->type()) { |
460 | #if QT_CONFIG(quicktemplates2_multitouch) |
461 | case QEvent::TouchBegin: |
462 | case QEvent::TouchUpdate: |
463 | case QEvent::TouchEnd: |
464 | handled = d->handleTouchEvent(source: item, event: static_cast<QTouchEvent *>(event), target: popup); |
465 | break; |
466 | #endif |
467 | case QEvent::HoverEnter: |
468 | case QEvent::HoverMove: |
469 | case QEvent::HoverLeave: |
470 | handled = d->handleHoverEvent(source: item, event: static_cast<QHoverEvent *>(event), target: popup); |
471 | break; |
472 | |
473 | case QEvent::MouseButtonPress: |
474 | case QEvent::MouseMove: |
475 | case QEvent::MouseButtonRelease: |
476 | handled = d->handleMouseEvent(source: item, event: static_cast<QMouseEvent *>(event), target: popup); |
477 | break; |
478 | |
479 | default: |
480 | break; |
481 | } |
482 | if (handled) |
483 | return true; |
484 | } |
485 | } |
486 | return false; |
487 | } |
488 | |
489 | bool QQuickOverlay::eventFilter(QObject *object, QEvent *event) |
490 | { |
491 | Q_D(QQuickOverlay); |
492 | if (!isVisible() || object != d->window) |
493 | return false; |
494 | |
495 | switch (event->type()) { |
496 | #if QT_CONFIG(quicktemplates2_multitouch) |
497 | case QEvent::TouchBegin: |
498 | case QEvent::TouchUpdate: |
499 | case QEvent::TouchEnd: |
500 | if (static_cast<QTouchEvent *>(event)->touchPointStates() & QEventPoint::Pressed) |
501 | emit pressed(); |
502 | if (static_cast<QTouchEvent *>(event)->touchPointStates() & QEventPoint::Released) |
503 | emit released(); |
504 | |
505 | // allow non-modal popups to close on touch release outside |
506 | if (!d->mouseGrabberPopup) { |
507 | for (const QTouchEvent::TouchPoint &point : static_cast<QTouchEvent *>(event)->points()) { |
508 | if (point.state() == QEventPoint::Released) { |
509 | if (d->handleRelease(source: d->window->contentItem(), event, target: nullptr)) |
510 | break; |
511 | } |
512 | } |
513 | } |
514 | |
515 | // setup currentEventDeliveryAgent like in QQuickDeliveryAgent::event |
516 | QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = d->deliveryAgent(); |
517 | d->deliveryAgentPrivate()->handleTouchEvent(static_cast<QTouchEvent *>(event)); |
518 | QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = nullptr; |
519 | |
520 | // If a touch event hasn't been accepted after being delivered, there |
521 | // were no items interested in touch events at any of the touch points. |
522 | // Make sure to accept the touch event in order to receive the consequent |
523 | // touch events, to be able to close non-modal popups on release outside. |
524 | event->accept(); |
525 | // Since we eat the event, QQuickWindow::event never sees it to clean up the |
526 | // grabber states. So we have to do so explicitly. |
527 | if (QQuickWindow *window = parentItem() ? parentItem()->window() : nullptr) { |
528 | QQuickWindowPrivate *d = QQuickWindowPrivate::get(c: window); |
529 | d->clearGrabbers(event: static_cast<QPointerEvent *>(event)); |
530 | } |
531 | return true; |
532 | #endif |
533 | |
534 | case QEvent::MouseButtonPress: |
535 | #if QT_CONFIG(quicktemplates2_multitouch) |
536 | // do not emit pressed() twice when mouse events have been synthesized from touch events |
537 | if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) |
538 | #endif |
539 | emit pressed(); |
540 | |
541 | // setup currentEventDeliveryAgent like in QQuickDeliveryAgent::event |
542 | QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = d->deliveryAgent(); |
543 | d->deliveryAgentPrivate()->handleMouseEvent(static_cast<QMouseEvent *>(event)); |
544 | QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = nullptr; |
545 | |
546 | // If a mouse event hasn't been accepted after being delivered, there |
547 | // was no item interested in mouse events at the mouse point. Make sure |
548 | // to accept the mouse event in order to receive the consequent mouse |
549 | // events, to be able to close non-modal popups on release outside. |
550 | event->accept(); |
551 | return true; |
552 | |
553 | case QEvent::MouseButtonRelease: |
554 | #if QT_CONFIG(quicktemplates2_multitouch) |
555 | // do not emit released() twice when mouse events have been synthesized from touch events |
556 | if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) |
557 | #endif |
558 | emit released(); |
559 | |
560 | // allow non-modal popups to close on mouse release outside |
561 | if (!d->mouseGrabberPopup) |
562 | d->handleRelease(source: d->window->contentItem(), event, target: nullptr); |
563 | break; |
564 | |
565 | #if QT_CONFIG(wheelevent) |
566 | case QEvent::Wheel: { |
567 | // If the top item in the drawing-order is blocked by a modal popup, then |
568 | // eat the event. There is no scenario where the top most item is blocked |
569 | // by a popup, but an item further down in the drawing order is not. |
570 | QWheelEvent *we = static_cast<QWheelEvent *>(event); |
571 | const QVector<QQuickItem *> targetItems = d->deliveryAgentPrivate()->pointerTargets( |
572 | d->window->contentItem(), event: we, point: we->point(i: 0), checkMouseButtons: false, checkAcceptsTouch: false); |
573 | if (targetItems.isEmpty()) |
574 | break; |
575 | |
576 | QQuickItem * const dimmerItem = property(name: "_q_dimmerItem" ).value<QQuickItem *>(); |
577 | QQuickItem * const topItem = targetItems.first(); |
578 | |
579 | QQuickItem *item = topItem; |
580 | while ((item = item->parentItem())) { |
581 | if (qobject_cast<QQuickPopupItem *>(object: item)) |
582 | break; |
583 | } |
584 | |
585 | if (!item && dimmerItem != topItem && isAncestorOf(child: topItem)) |
586 | break; |
587 | |
588 | const auto = d->stackingOrderPopups(); |
589 | // Eat the event if receiver topItem is not a child of a popup before |
590 | // the first modal popup. |
591 | for (const auto & : popups) { |
592 | const QQuickItem * = popup->popupItem(); |
593 | if (!popupItem) |
594 | continue; |
595 | // if current popup item matches with any popup in stack, deliver the event |
596 | if (popupItem == item) |
597 | break; |
598 | // if the popup doesn't contain the item but is modal, eat the event |
599 | if (popup->overlayEvent(item: topItem, event: we)) |
600 | return true; |
601 | } |
602 | break; |
603 | } |
604 | #endif |
605 | |
606 | default: |
607 | break; |
608 | } |
609 | |
610 | return false; |
611 | } |
612 | |
613 | class QQuickOverlayAttachedPrivate : public QObjectPrivate |
614 | { |
615 | public: |
616 | Q_DECLARE_PUBLIC(QQuickOverlayAttached) |
617 | |
618 | void setWindow(QQuickWindow *newWindow); |
619 | |
620 | QQuickWindow *window = nullptr; |
621 | QQmlComponent *modal = nullptr; |
622 | QQmlComponent *modeless = nullptr; |
623 | }; |
624 | |
625 | void QQuickOverlayAttachedPrivate::setWindow(QQuickWindow *newWindow) |
626 | { |
627 | Q_Q(QQuickOverlayAttached); |
628 | if (window == newWindow) |
629 | return; |
630 | |
631 | if (QQuickOverlay *oldOverlay = QQuickOverlay::overlay(window)) { |
632 | QObject::disconnect(sender: oldOverlay, signal: &QQuickOverlay::pressed, receiver: q, slot: &QQuickOverlayAttached::pressed); |
633 | QObject::disconnect(sender: oldOverlay, signal: &QQuickOverlay::released, receiver: q, slot: &QQuickOverlayAttached::released); |
634 | } |
635 | |
636 | if (QQuickOverlay *newOverlay = QQuickOverlay::overlay(window: newWindow)) { |
637 | QObject::connect(sender: newOverlay, signal: &QQuickOverlay::pressed, context: q, slot: &QQuickOverlayAttached::pressed); |
638 | QObject::connect(sender: newOverlay, signal: &QQuickOverlay::released, context: q, slot: &QQuickOverlayAttached::released); |
639 | } |
640 | |
641 | window = newWindow; |
642 | emit q->overlayChanged(); |
643 | } |
644 | |
645 | /*! |
646 | \qmlattachedsignal QtQuick.Controls::Overlay::pressed() |
647 | |
648 | This attached signal is emitted when the overlay is pressed by the user while |
649 | a popup is visible. |
650 | |
651 | The signal can be attached to any item, popup, or window. When attached to an |
652 | item or a popup, the signal is only emitted if the item or popup is in a window. |
653 | */ |
654 | |
655 | /*! |
656 | \qmlattachedsignal QtQuick.Controls::Overlay::released() |
657 | |
658 | This attached signal is emitted when the overlay is released by the user while |
659 | a popup is visible. |
660 | |
661 | The signal can be attached to any item, popup, or window. When attached to an |
662 | item or a popup, the signal is only emitted if the item or popup is in a window. |
663 | */ |
664 | |
665 | QQuickOverlayAttached::QQuickOverlayAttached(QObject *parent) |
666 | : QObject(*(new QQuickOverlayAttachedPrivate), parent) |
667 | { |
668 | Q_D(QQuickOverlayAttached); |
669 | if (QQuickItem *item = qobject_cast<QQuickItem *>(o: parent)) { |
670 | d->setWindow(item->window()); |
671 | QObjectPrivate::connect(sender: item, signal: &QQuickItem::windowChanged, receiverPrivate: d, slot: &QQuickOverlayAttachedPrivate::setWindow); |
672 | } else if (QQuickPopup * = qobject_cast<QQuickPopup *>(object: parent)) { |
673 | d->setWindow(popup->window()); |
674 | QObjectPrivate::connect(sender: popup, signal: &QQuickPopup::windowChanged, receiverPrivate: d, slot: &QQuickOverlayAttachedPrivate::setWindow); |
675 | } else { |
676 | d->setWindow(qobject_cast<QQuickWindow *>(object: parent)); |
677 | } |
678 | } |
679 | |
680 | /*! |
681 | \qmlattachedproperty Overlay QtQuick.Controls::Overlay::overlay |
682 | \readonly |
683 | |
684 | This attached property holds the window overlay item. |
685 | |
686 | The property can be attached to any item, popup, or window. When attached to an |
687 | item or a popup, the value is \c null if the item or popup is not in a window. |
688 | */ |
689 | QQuickOverlay *QQuickOverlayAttached::overlay() const |
690 | { |
691 | Q_D(const QQuickOverlayAttached); |
692 | return QQuickOverlay::overlay(window: d->window); |
693 | } |
694 | |
695 | /*! |
696 | \qmlattachedproperty Component QtQuick.Controls::Overlay::modal |
697 | |
698 | This attached property holds a component to use as a visual item that implements |
699 | background dimming for modal popups. It is created for and stacked below visible |
700 | modal popups. |
701 | |
702 | The property can be attached to any popup. |
703 | |
704 | For example, to change the color of the background dimming for a modal |
705 | popup, the following code can be used: |
706 | |
707 | \snippet qtquickcontrols-overlay-modal.qml 1 |
708 | |
709 | \sa Popup::modal |
710 | */ |
711 | QQmlComponent *QQuickOverlayAttached::modal() const |
712 | { |
713 | Q_D(const QQuickOverlayAttached); |
714 | return d->modal; |
715 | } |
716 | |
717 | void QQuickOverlayAttached::setModal(QQmlComponent *modal) |
718 | { |
719 | Q_D(QQuickOverlayAttached); |
720 | if (d->modal == modal) |
721 | return; |
722 | |
723 | d->modal = modal; |
724 | emit modalChanged(); |
725 | } |
726 | |
727 | /*! |
728 | \qmlattachedproperty Component QtQuick.Controls::Overlay::modeless |
729 | |
730 | This attached property holds a component to use as a visual item that implements |
731 | background dimming for modeless popups. It is created for and stacked below visible |
732 | dimming popups. |
733 | |
734 | The property can be attached to any popup. |
735 | |
736 | For example, to change the color of the background dimming for a modeless |
737 | popup, the following code can be used: |
738 | |
739 | \snippet qtquickcontrols-overlay-modeless.qml 1 |
740 | |
741 | \sa Popup::dim |
742 | */ |
743 | QQmlComponent *QQuickOverlayAttached::modeless() const |
744 | { |
745 | Q_D(const QQuickOverlayAttached); |
746 | return d->modeless; |
747 | } |
748 | |
749 | void QQuickOverlayAttached::setModeless(QQmlComponent *modeless) |
750 | { |
751 | Q_D(QQuickOverlayAttached); |
752 | if (d->modeless == modeless) |
753 | return; |
754 | |
755 | d->modeless = modeless; |
756 | emit modelessChanged(); |
757 | } |
758 | |
759 | QT_END_NAMESPACE |
760 | |
761 | #include "moc_qquickoverlay_p.cpp" |
762 | |