13#include <QtGui/private/qguiapplication_p.h>
15#include <QtCore/qloggingcategory.h>
16#include <QtGui/private/qeventpoint_p.h>
17#include <QtQuick/private/qquickitem_p.h>
18#include <QtQuick/private/qquickwindowmodule_p.h>
19#include <QtQuick/private/qquickwindowmodule_p_p.h>
20#include <qpa/qplatformwindow_p.h>
24Q_STATIC_LOGGING_CATEGORY(lcPopupWindow,
"qt.quick.controls.popup.window")
26static bool s_popupGrabOk =
false;
31 Q_DECLARE_PUBLIC(QQuickPopupWindow)
43 bool filterPopupSpecialCases(QEvent *event);
46QQuickPopupWindow::QQuickPopupWindow(QQuickPopup *popup, QWindow *parent)
47 : QQuickWindowQmlImpl(*(
new QQuickPopupWindowPrivate),
nullptr)
49 Q_D(QQuickPopupWindow);
52 d->m_popupItem = popup->popupItem();
53 setTransientParent(parent);
55 connect(d->m_popup, &QQuickPopup::windowChanged,
this, &QQuickPopupWindow::windowChanged);
56 connect(d->m_popup, &QQuickPopup::implicitWidthChanged,
this, &QQuickPopupWindow::implicitWidthChanged);
57 connect(d->m_popup, &QQuickPopup::implicitHeightChanged,
this, &QQuickPopupWindow::implicitHeightChanged);
58 if (QQuickWindow *nearestParentItemWindow = d->m_popup->window()) {
59 d->m_popupParentItemWindow = nearestParentItemWindow;
60 connect(d->m_popupParentItemWindow, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
61 connect(d->m_popupParentItemWindow, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
63 setWidth(d->m_popupItem->implicitWidth());
64 setHeight(d->m_popupItem->implicitHeight());
66 const auto flags = QQuickPopupPrivate::get(popup)->popupWindowType();
69 if (flags & Qt::Popup)
70 setColor(QColorConstants::Transparent);
74 qCDebug(lcPopupWindow) <<
"Created popup window" <<
this <<
"with parent" << parent;
77QQuickPopup *QQuickPopupWindow::popup()
const
79 Q_D(
const QQuickPopupWindow);
83void QQuickPopupWindow::hideEvent(QHideEvent *e)
85 Q_D(QQuickPopupWindow);
86 QQuickWindow::hideEvent(e);
88 QScopedValueRollback<
bool>inHideEventRollback(d->m_inHideEvent,
true);
89 if (QQuickPopup *popup = d->m_popup) {
90 QQuickDialog *dialog = qobject_cast<QQuickDialog *>(popup);
91 if (dialog && QQuickPopupPrivate::get(dialog)->visible)
94 popup->setVisible(
false);
98void QQuickPopupWindow::moveEvent(QMoveEvent *e)
100 handlePopupPositionChangeFromWindowSystem(e->pos());
103void QQuickPopupWindow::resizeEvent(QResizeEvent *e)
105 Q_D(QQuickPopupWindow);
106 QQuickWindowQmlImpl::resizeEvent(e);
111 qCDebug(lcPopupWindow) <<
"A window system event changed the popup's size to be " << e->size();
112 QQuickPopupPrivate *popupPrivate = QQuickPopupPrivate::get(d->m_popup);
114 const auto topLeftFromSystem = global2Local(d->geometry.topLeft());
117 const auto oldX = popupPrivate->x;
118 const auto oldY = popupPrivate->y;
120 if (Q_LIKELY(topLeftFromSystem)) {
121 popupPrivate->x = topLeftFromSystem->x();
122 popupPrivate->y = topLeftFromSystem->y();
125 const QMarginsF windowInsets = popupPrivate->windowInsets();
126 d->m_popupItem->setWidth(e->size().width() - windowInsets.left() - windowInsets.right());
127 d->m_popupItem->setHeight(e->size().height() - windowInsets.top() - windowInsets.bottom());
130 popupPrivate->x = oldX;
131 popupPrivate->y = oldY;
136 Q_Q(QQuickPopupWindow);
137 const bool isTransientParentDestroyed = !q->transientParent() ?
true :
138 QQuickWindowPrivate::get(qobject_cast<QQuickWindow *>(q->transientParent()))->inDestructor;
142 const bool visibleChanged = QWindowPrivate::visible != visible;
145 if (!visible && visibleChanged && QGuiApplicationPrivate::popupCount() == 1 && s_grabbedWindow) {
148 s_popupGrabOk =
false;
149 qCDebug(lcPopupWindow) <<
"The window " << s_grabbedWindow <<
"has disabled global mouse and keyboard grabs.";
153 QQuickWindowQmlImplPrivate::setVisible(visible);
159 if (visible && visibleChanged && QGuiApplicationPrivate::popupCount() == 1 && !s_popupGrabOk) {
160 QWindow *win = m_popup->window();
161 if (QGuiApplication::platformName() == QStringLiteral(
"offscreen"))
163 s_popupGrabOk = win->setKeyboardGrabEnabled(
true);
165 s_popupGrabOk = win->setMouseGrabEnabled(
true);
167 win->setKeyboardGrabEnabled(
false);
169 qCDebug(lcPopupWindow) <<
"The window" << win <<
"has enabled global mouse" << (s_popupGrabOk ?
"and keyboard" :
"") <<
"grabs.";
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
198 Q_Q(QQuickPopupWindow);
200 if (!event->isPointerEvent())
203 QQuickPopup *popup = m_popup;
207 auto *pe =
static_cast<QPointerEvent *>(event);
208 const QPointF globalPos = pe->points().first().globalPosition();
209 const QQuickPopup::ClosePolicy closePolicy = popup->closePolicy();
210 QQuickPopup *targetPopup = QQuickPopupPrivate::get(popup)->contains(contentItem->mapFromGlobal(globalPos)) ? popup :
nullptr;
213 QQuickMenu *menu = qobject_cast<QQuickMenu *>(popup);
214 QQuickMenuBar *targetMenuBar =
nullptr;
215 QObject *menuParent = menu;
217 if (
auto *parentMenu = qobject_cast<QQuickMenu *>(menuParent)) {
218 QQuickPopupWindow *popupWindow = QQuickMenuPrivate::get(parentMenu)->popupWindow;
219 auto *popup_d = QQuickPopupPrivate::get(popupWindow->popup());
220 QPointF scenePos = popupWindow->contentItem()->mapFromGlobal(globalPos);
221 if (popup_d->contains(scenePos)) {
222 targetPopup = parentMenu;
225 }
else if (
auto *menuBar = qobject_cast<QQuickMenuBar *>(menuParent)) {
226 const QPointF menuBarPos = menuBar->mapFromGlobal(globalPos);
227 if (menuBar->contains(menuBarPos))
228 targetMenuBar = menuBar;
232 menuParent = menuParent->parent();
235 auto closePopupAndParentMenus = [q]() {
236 QQuickPopup *current = q->popup();
238 qCDebug(lcPopupWindow) <<
"Closing" << current <<
"from an outside pointer press or release event";
240 current = qobject_cast<QQuickMenu *>(current->parent());
244 if (pe->isBeginEvent()) {
251 closePopupAndParentMenus();
253 }
else if (!targetPopup && closePolicy.testAnyFlags(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent)) {
258 closePopupAndParentMenus();
261 }
else if (pe->isUpdateEvent()){
262 QQuickWindow *targetWindow =
nullptr;
264 targetWindow = QQuickPopupPrivate::get(targetPopup)->popupWindow;
265 else if (targetMenuBar)
266 targetWindow = targetMenuBar->window();
271 const auto scenePos = pe->point(0).scenePosition();
272 const auto translatedScenePos = targetWindow->mapFromGlobal(globalPos);
273 QMutableEventPoint::setScenePosition(pe->point(0), translatedScenePos);
274 auto *grabber = pe->exclusiveGrabber(pe->point(0));
281 pe->setExclusiveGrabber(pe->point(0),
nullptr);
284 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetWindow;
285 QQuickWindowPrivate::get(targetWindow)->deliveryAgent->event(pe);
288 QMutableEventPoint::setScenePosition(pe->point(0), scenePos);
290 pe->setExclusiveGrabber(pe->point(0), grabber);
291 }
else if (pe->isEndEvent()) {
292 if (!targetPopup && !targetMenuBar && closePolicy.testAnyFlags(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent)) {
294 closePopupAndParentMenus();
301 if (QQuickMenu *targetMenu = qobject_cast<QQuickMenu *>(targetPopup)) {
302 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetMenu;
303 QQuickMenuPrivate::get(targetMenu)->handleReleaseWithoutGrab(pe->point(0));
310bool QQuickPopupWindow::event(QEvent *e)
312 Q_D(QQuickPopupWindow);
313 if (d->filterPopupSpecialCases(e))
316 if (QQuickPopup *popup = d->m_popup) {
318 if (!popup->hasFocus() && (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease)
319#if QT_CONFIG(shortcut)
320 && (!
static_cast<QKeyEvent *>(e)->matches(QKeySequence::Cancel)
321#if defined(Q_OS_ANDROID)
322 ||
static_cast<QKeyEvent *>(e)->key() != Qt::Key_Back
329 return QQuickWindowQmlImpl::event(e);
332void QQuickPopupWindow::windowChanged(QWindow *window)
334 Q_D(QQuickPopupWindow);
335 if (!d->m_popupParentItemWindow.isNull()) {
336 disconnect(d->m_popupParentItemWindow, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
337 disconnect(d->m_popupParentItemWindow, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
340 d->m_popupParentItemWindow = window;
341 connect(window, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
342 connect(window, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
344 d->m_popupParentItemWindow.clear();
348std::optional<QPoint> QQuickPopupWindow::global2Local(
const QPoint &pos)
const
350 Q_D(
const QQuickPopupWindow);
351 QQuickPopup *popup = d->m_popup;
353 QWindow *mainWindow = d->m_popupParentItemWindow;
355 mainWindow = transientParent();
356 if (Q_UNLIKELY((!mainWindow || mainWindow != popup->window())))
359 const QPoint scenePos = mainWindow->mapFromGlobal(pos);
361 return popup->parentItem() ? popup->parentItem()->mapFromScene(scenePos).toPoint() : scenePos;
364void QQuickPopupWindow::parentWindowXChanged(
int newX)
366 const auto popupLocalPos = global2Local({x(), y()});
367 if (Q_UNLIKELY(!popupLocalPos))
369 handlePopupPositionChangeFromWindowSystem({ newX + popupLocalPos->x(), y() });
372void QQuickPopupWindow::parentWindowYChanged(
int newY)
374 const auto popupLocalPos = global2Local({x(), y()});
375 if (Q_UNLIKELY(!popupLocalPos))
377 handlePopupPositionChangeFromWindowSystem({ x(), newY + popupLocalPos->y() });
380void QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem(
const QPoint &pos)
382 Q_D(QQuickPopupWindow);
383 QQuickPopup *popup = d->m_popup;
387 const auto windowPos = global2Local(pos);
388 if (Q_LIKELY(windowPos)) {
389 qCDebug(lcPopupWindow) <<
"A window system event changed the popup's position to be " << *windowPos;
390 QQuickPopupPrivate::get(popup)->setEffectivePosFromWindowPos(*windowPos);
394void QQuickPopupWindow::implicitWidthChanged()
396 Q_D(
const QQuickPopupWindow);
397 if (
auto popup = d->m_popup)
398 setWidth(popup->implicitWidth());
401void QQuickPopupWindow::implicitHeightChanged()
403 Q_D(
const QQuickPopupWindow);
404 if (
auto popup = d->m_popup)
405 setHeight(popup->implicitHeight());