6#if QT_CONFIG(quicktemplates2_container)
7#include "qquickdialog_p.h"
13#if QT_CONFIG(quicktemplates2_container)
14#include "qquickmenubar_p_p.h"
18#include <QtGui/private/qguiapplication_p.h>
20#include <QtCore/qloggingcategory.h>
21#include <QtGui/private/qeventpoint_p.h>
22#include <QtQuick/private/qquickitem_p.h>
23#include <QtQuick/private/qquickwindowmodule_p.h>
24#include <QtQuick/private/qquickwindowmodule_p_p.h>
25#include <qpa/qplatformwindow_p.h>
29Q_STATIC_LOGGING_CATEGORY(lcPopupWindow,
"qt.quick.controls.popup.window")
31static bool s_popupGrabOk =
false;
36 Q_DECLARE_PUBLIC(QQuickPopupWindow)
48 bool filterPopupSpecialCases(QEvent *event);
51QQuickPopupWindow::QQuickPopupWindow(QQuickPopup *popup, QWindow *parent)
52 : QQuickWindowQmlImpl(*(
new QQuickPopupWindowPrivate),
nullptr)
54 Q_D(QQuickPopupWindow);
57 d->m_popupItem = popup->popupItem();
58 setTransientParent(parent);
60 connect(d->m_popup, &QQuickPopup::windowChanged,
this, &QQuickPopupWindow::windowChanged);
61 connect(d->m_popup, &QQuickPopup::implicitWidthChanged,
this, &QQuickPopupWindow::implicitWidthChanged);
62 connect(d->m_popup, &QQuickPopup::implicitHeightChanged,
this, &QQuickPopupWindow::implicitHeightChanged);
63 if (QQuickWindow *nearestParentItemWindow = d->m_popup->window()) {
64 d->m_popupParentItemWindow = nearestParentItemWindow;
65 connect(d->m_popupParentItemWindow, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
66 connect(d->m_popupParentItemWindow, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
68 setWidth(d->m_popupItem->implicitWidth());
69 setHeight(d->m_popupItem->implicitHeight());
71 const auto flags = QQuickPopupPrivate::get(popup)->popupWindowFlags();
74 if (flags & Qt::Popup)
75 setColor(QColorConstants::Transparent);
79 qCDebug(lcPopupWindow) <<
"Created popup window" <<
this <<
"with parent" << parent;
82QQuickPopup *QQuickPopupWindow::popup()
const
84 Q_D(
const QQuickPopupWindow);
88void QQuickPopupWindow::hideEvent(QHideEvent *e)
90 Q_D(QQuickPopupWindow);
91 QQuickWindow::hideEvent(e);
93 QScopedValueRollback<
bool>inHideEventRollback(d->m_inHideEvent,
true);
94 if (QQuickPopup *popup = d->m_popup) {
95#if QT_CONFIG(quicktemplates2_container)
96 QQuickDialog *dialog = qobject_cast<QQuickDialog *>(popup);
97 if (dialog && QQuickPopupPrivate::get(dialog)->visible)
101 popup->setVisible(
false);
105void QQuickPopupWindow::moveEvent(QMoveEvent *e)
107 handlePopupPositionChangeFromWindowSystem(e->pos());
110void QQuickPopupWindow::resizeEvent(QResizeEvent *e)
112 Q_D(QQuickPopupWindow);
113 QQuickWindowQmlImpl::resizeEvent(e);
118 qCDebug(lcPopupWindow).nospace() <<
"A window system event changed the popup's ("
119 << d->m_popup <<
") size to " << e->size();
120 QQuickPopupPrivate *popupPrivate = QQuickPopupPrivate::get(d->m_popup);
122 const auto topLeftFromSystem = global2Local(d->geometry.topLeft());
125 const auto oldX = popupPrivate->x;
126 const auto oldY = popupPrivate->y;
128 if (Q_LIKELY(topLeftFromSystem)) {
129 popupPrivate->x = topLeftFromSystem->x();
130 popupPrivate->y = topLeftFromSystem->y();
133 const QMarginsF windowInsets = popupPrivate->windowInsets();
134 d->m_popupItem->setWidth(e->size().width() - windowInsets.left() - windowInsets.right());
135 d->m_popupItem->setHeight(e->size().height() - windowInsets.top() - windowInsets.bottom());
138 popupPrivate->x = oldX;
139 popupPrivate->y = oldY;
144 Q_Q(QQuickPopupWindow);
145 const bool isTransientParentDestroyed = !q->transientParent() ?
true :
146 QQuickWindowPrivate::get(qobject_cast<QQuickWindow *>(q->transientParent()))->inDestructor;
150 const bool visibleChanged = QWindowPrivate::visible != visible;
153 if (!visible && visibleChanged && QGuiApplicationPrivate::popupCount() == 1 && s_grabbedWindow) {
156 s_popupGrabOk =
false;
157 qCDebug(lcPopupWindow) <<
"The window " << s_grabbedWindow <<
"has disabled global mouse and keyboard grabs.";
161#if QT_CONFIG(wayland)
163 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(platformWindow); waylandWindow && visible)
164 waylandWindow->setParentControlGeometry(q->parentControlGeometry());
167 QQuickWindowQmlImplPrivate::setVisible(visible);
173 if (visible && visibleChanged && QGuiApplicationPrivate::popupCount() == 1 && !s_popupGrabOk) {
174 QWindow *win = m_popup->window();
175 if (QGuiApplication::platformName() == QStringLiteral(
"offscreen"))
177 s_popupGrabOk = win->setKeyboardGrabEnabled(
true);
179 s_popupGrabOk = win->setMouseGrabEnabled(
true);
181 win->setKeyboardGrabEnabled(
false);
183 qCDebug(lcPopupWindow) <<
"The window" << win <<
"has enabled global mouse" << (s_popupGrabOk ?
"and keyboard" :
"") <<
"grabs.";
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
213#if QT_CONFIG(quicktemplates2_container)
214 Q_Q(QQuickPopupWindow);
216 if (!event->isPointerEvent())
219 QQuickPopup *popup = m_popup;
223 auto *pe =
static_cast<QPointerEvent *>(event);
224 const QPointF globalPos = pe->points().first().globalPosition();
225 QQuickPopup *targetPopup = QQuickPopupPrivate::get(popup)->contains(contentItem->mapFromGlobal(globalPos)) ? popup :
nullptr;
228 QQuickMenu *menu = qobject_cast<QQuickMenu *>(popup);
229 QQuickMenuBar *targetMenuBar =
nullptr;
230 QObject *menuParent = menu;
232 if (
auto *parentMenu = qobject_cast<QQuickMenu *>(menuParent)) {
233 QQuickPopupWindow *popupWindow = QQuickMenuPrivate::get(parentMenu)->popupWindow;
234 auto *popup_d = QQuickPopupPrivate::get(popupWindow->popup());
235 QPointF scenePos = popupWindow->contentItem()->mapFromGlobal(globalPos);
236 if (popup_d->contains(scenePos)) {
237 targetPopup = parentMenu;
240 }
else if (
auto *menuBar = qobject_cast<QQuickMenuBar *>(menuParent)) {
241 const QPointF menuBarPos = menuBar->mapFromGlobal(globalPos);
242 if (menuBar->contains(menuBarPos))
243 targetMenuBar = menuBar;
247 menuParent = menuParent->parent();
250 auto closePopups = [q, globalPos](QQuickPopup::ClosePolicy closePolicy) {
251 static const QQuickPopup::ClosePolicy outsideParentFlags =
252 QQuickPopup::CloseOnPressOutsideParent | QQuickPopup::CloseOnReleaseOutsideParent;
254 QQuickPopup *current = q->popup();
255 while (current && current->closePolicy().testAnyFlags(closePolicy)
256 && QQuickPopupPrivate::get(current)->popupWindow) {
257 auto *currentPrivate = QQuickPopupPrivate::get(current);
258 const QPointF scenePos = currentPrivate->popupWindow->contentItem()->mapFromGlobal(globalPos);
259 if (currentPrivate->contains(scenePos))
266 if (current->closePolicy().testAnyFlags(closePolicy & outsideParentFlags)) {
267 const QQuickItem *parentItem = current->parentItem();
268 if (parentItem && parentItem->contains(parentItem->mapFromGlobal(globalPos)))
272 qCDebug(lcPopupWindow)
273 <<
"Closing" << current <<
"from an outside pointer press or release event.";
275 auto *transientParentPopupWindow = qobject_cast<QQuickPopupWindow *>(
276 currentPrivate->popupWindow->transientParent());
277 current = current->closePolicy().testAnyFlag(QQuickPopup::CloseMultiple)
278 && transientParentPopupWindow
279 ? transientParentPopupWindow->popup()
284 if (pe->isBeginEvent()) {
291 closePopups(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
293 }
else if (!targetPopup) {
299 if (event->type() != QEvent::NonClientAreaMouseButtonPress && event->type() != QEvent::NonClientAreaMouseButtonDblClick) {
300 closePopups(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
304 if (popup->isModal()) {
311 }
else if (pe->isUpdateEvent()){
312 QQuickWindow *targetWindow =
nullptr;
314 targetWindow = QQuickPopupPrivate::get(targetPopup)->popupWindow;
315 else if (targetMenuBar)
316 targetWindow = targetMenuBar->window();
323 if (targetWindow == q)
327 const auto scenePos = pe->point(0).scenePosition();
328 const auto translatedScenePos = targetWindow->mapFromGlobal(globalPos);
329 QMutableEventPoint::setScenePosition(pe->point(0), translatedScenePos);
330 auto *grabber = pe->exclusiveGrabber(pe->point(0));
337 pe->setExclusiveGrabber(pe->point(0),
nullptr);
340 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetWindow;
341 QQuickWindowPrivate::get(targetWindow)->deliveryAgent->event(pe);
344 QMutableEventPoint::setScenePosition(pe->point(0), scenePos);
346 pe->setExclusiveGrabber(pe->point(0), grabber);
347 }
else if (pe->isEndEvent()) {
348 if (!targetPopup && !targetMenuBar) {
351 if (event->type() != QEvent::NonClientAreaMouseButtonRelease)
352 closePopups(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
359 if (QQuickMenu *targetMenu = qobject_cast<QQuickMenu *>(targetPopup)) {
360 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetMenu;
361 QQuickMenuPrivate::get(targetMenu)->handleReleaseWithoutGrab(pe->point(0));
369bool QQuickPopupWindow::event(QEvent *e)
371 Q_D(QQuickPopupWindow);
372 if (e->type() == QEvent::PlatformSurface &&
static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
373#if QT_CONFIG(wayland)
374 if (
auto *waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(handle())) {
375 waylandWindow->setExtendedWindowType(QQuickPopupPrivate::get(d->m_popup)->extendedWindowType);
376 waylandWindow->setParentControlGeometry(parentControlGeometry());
380 if (
auto *xcbWindow =
dynamic_cast<QNativeInterface::Private::QXcbWindow *>(handle())) {
381 const auto xcbWindowType = QQuickPopupPrivate::get(d->m_popup)->wmWindowType;
382 if (xcbWindowType != QNativeInterface::Private::QXcbWindow::None)
383 xcbWindow->setWindowType(xcbWindowType);
388 if (d->filterPopupSpecialCases(e))
391 if (QQuickPopup *popup = d->m_popup) {
393 if (!popup->hasFocus() && (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease)
394#if QT_CONFIG(shortcut)
395 && (!
static_cast<QKeyEvent *>(e)->matches(QKeySequence::Cancel)
396#if defined(Q_OS_ANDROID)
397 ||
static_cast<QKeyEvent *>(e)->key() != Qt::Key_Back
404 return QQuickWindowQmlImpl::event(e);
407void QQuickPopupWindow::windowChanged(QWindow *window)
409 Q_D(QQuickPopupWindow);
410 if (!d->m_popupParentItemWindow.isNull()) {
411 disconnect(d->m_popupParentItemWindow, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
412 disconnect(d->m_popupParentItemWindow, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
415 d->m_popupParentItemWindow = window;
416 connect(window, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
417 connect(window, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
419 d->m_popupParentItemWindow.clear();
421 setTransientParent(window);
424std::optional<QPoint> QQuickPopupWindow::global2Local(
const QPoint &pos)
const
426 Q_D(
const QQuickPopupWindow);
427 QQuickPopup *popup = d->m_popup;
429 QWindow *mainWindow = d->m_popupParentItemWindow;
431 mainWindow = transientParent();
432 if (Q_UNLIKELY((!mainWindow || mainWindow != popup->window())))
435 const QPoint scenePos = mainWindow->mapFromGlobal(pos);
437 return popup->parentItem() ? popup->parentItem()->mapFromScene(scenePos).toPoint() : scenePos;
440void QQuickPopupWindow::parentWindowXChanged(
int newX)
442 const auto popupLocalPos = global2Local({x(), y()});
443 if (Q_UNLIKELY(!popupLocalPos))
445 handlePopupPositionChangeFromWindowSystem({ newX + popupLocalPos->x(), y() });
448void QQuickPopupWindow::parentWindowYChanged(
int newY)
450 const auto popupLocalPos = global2Local({x(), y()});
451 if (Q_UNLIKELY(!popupLocalPos))
453 handlePopupPositionChangeFromWindowSystem({ x(), newY + popupLocalPos->y() });
456void QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem(
const QPoint &pos)
458 Q_D(QQuickPopupWindow);
459 QQuickPopup *popup = d->m_popup;
463 const auto windowPos = global2Local(pos);
464 if (Q_LIKELY(windowPos)) {
465 qCDebug(lcPopupWindow).nospace() <<
"A window system event changed the popup's"
466 <<
" (" << d->m_popup <<
") position to " << *windowPos;
467 QQuickPopupPrivate::get(popup)->setEffectivePosFromWindowPos(*windowPos);
471void QQuickPopupWindow::implicitWidthChanged()
473 Q_D(QQuickPopupWindow);
474 if (
auto popup = d->m_popup) {
475 auto *popupPrivate = QQuickPopupPrivate::get(popup);
481 const QMarginsF insets = popupPrivate->windowInsets();
482 if (!popupPrivate->hasWidth) {
483 setWidth(qCeil(popup->implicitWidth() + insets.left() + insets.right()));
484 d->m_popupItem->setWidth(popup->implicitWidth());
486 popupPrivate->reposition();
490void QQuickPopupWindow::implicitHeightChanged()
492 Q_D(QQuickPopupWindow);
493 if (
auto popup = d->m_popup) {
494 auto *popupPrivate = QQuickPopupPrivate::get(popup);
495 const QMarginsF insets = popupPrivate->windowInsets();
496 if (!popupPrivate->hasHeight) {
497 setHeight(qCeil(popup->implicitHeight() + insets.top() + insets.bottom()));
498 d->m_popupItem->setHeight(popup->implicitHeight());
500 popupPrivate->reposition();
504#if QT_CONFIG(wayland)
505QRect QQuickPopupWindow::parentControlGeometry()
const
507 const QQuickItem *parent = popup()->parentItem();
510 const qreal overlap = popup()->property(
"overlap").toReal();
511 QRectF parentItemBoundingRect = parent->boundingRect();
512 const QPointF parentItemMappedPosition = parent->mapToScene(parentItemBoundingRect.topLeft());
513 return { qFloor(parentItemMappedPosition.x() + overlap), qFloor(parentItemMappedPosition.y()),
514 qCeil(qMax<qreal>(qAbs(parentItemBoundingRect.width() - overlap * 2), parentItemBoundingRect.width() / 4)), qCeil(parentItemBoundingRect.height()) };
Combined button and popup list for selecting options.