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 const QQuickPopup::ClosePolicy closePolicy = popup->closePolicy();
226 QQuickPopup *targetPopup = QQuickPopupPrivate::get(popup)->contains(contentItem->mapFromGlobal(globalPos)) ? popup :
nullptr;
229 QQuickMenu *menu = qobject_cast<QQuickMenu *>(popup);
230 QQuickMenuBar *targetMenuBar =
nullptr;
231 QObject *menuParent = menu;
233 if (
auto *parentMenu = qobject_cast<QQuickMenu *>(menuParent)) {
234 QQuickPopupWindow *popupWindow = QQuickMenuPrivate::get(parentMenu)->popupWindow;
235 auto *popup_d = QQuickPopupPrivate::get(popupWindow->popup());
236 QPointF scenePos = popupWindow->contentItem()->mapFromGlobal(globalPos);
237 if (popup_d->contains(scenePos)) {
238 targetPopup = parentMenu;
241 }
else if (
auto *menuBar = qobject_cast<QQuickMenuBar *>(menuParent)) {
242 const QPointF menuBarPos = menuBar->mapFromGlobal(globalPos);
243 if (menuBar->contains(menuBarPos))
244 targetMenuBar = menuBar;
248 menuParent = menuParent->parent();
251 auto closePopupAndParentMenus = [q]() {
252 QQuickPopup *current = q->popup();
254 qCDebug(lcPopupWindow) <<
"Closing" << current <<
"from an outside pointer press or release event";
256 current = qobject_cast<QQuickMenu *>(current->parent());
260 if (pe->isBeginEvent()) {
267 closePopupAndParentMenus();
269 }
else if (!targetPopup) {
275 if (event->type() != QEvent::NonClientAreaMouseButtonPress && event->type() != QEvent::NonClientAreaMouseButtonDblClick) {
276 if (closePolicy.testAnyFlags(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent))
277 closePopupAndParentMenus();
281 if (popup->isModal()) {
288 }
else if (pe->isUpdateEvent()){
289 QQuickWindow *targetWindow =
nullptr;
291 targetWindow = QQuickPopupPrivate::get(targetPopup)->popupWindow;
292 else if (targetMenuBar)
293 targetWindow = targetMenuBar->window();
300 if (targetWindow == q)
304 const auto scenePos = pe->point(0).scenePosition();
305 const auto translatedScenePos = targetWindow->mapFromGlobal(globalPos);
306 QMutableEventPoint::setScenePosition(pe->point(0), translatedScenePos);
307 auto *grabber = pe->exclusiveGrabber(pe->point(0));
314 pe->setExclusiveGrabber(pe->point(0),
nullptr);
317 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetWindow;
318 QQuickWindowPrivate::get(targetWindow)->deliveryAgent->event(pe);
321 QMutableEventPoint::setScenePosition(pe->point(0), scenePos);
323 pe->setExclusiveGrabber(pe->point(0), grabber);
324 }
else if (pe->isEndEvent()) {
325 if (!targetPopup && !targetMenuBar && closePolicy.testAnyFlags(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent)) {
328 if (event->type() != QEvent::NonClientAreaMouseButtonRelease)
329 closePopupAndParentMenus();
336 if (QQuickMenu *targetMenu = qobject_cast<QQuickMenu *>(targetPopup)) {
337 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetMenu;
338 QQuickMenuPrivate::get(targetMenu)->handleReleaseWithoutGrab(pe->point(0));
346bool QQuickPopupWindow::event(QEvent *e)
348 Q_D(QQuickPopupWindow);
349 if (e->type() == QEvent::PlatformSurface &&
static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
350#if QT_CONFIG(wayland)
351 if (
auto *waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(handle())) {
352 waylandWindow->setExtendedWindowType(QQuickPopupPrivate::get(d->m_popup)->extendedWindowType);
353 waylandWindow->setParentControlGeometry(parentControlGeometry());
357 if (
auto *xcbWindow =
dynamic_cast<QNativeInterface::Private::QXcbWindow *>(handle())) {
358 const auto xcbWindowType = QQuickPopupPrivate::get(d->m_popup)->wmWindowType;
359 if (xcbWindowType != QNativeInterface::Private::QXcbWindow::None)
360 xcbWindow->setWindowType(xcbWindowType);
365 if (d->filterPopupSpecialCases(e))
368 if (QQuickPopup *popup = d->m_popup) {
370 if (!popup->hasFocus() && (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease)
371#if QT_CONFIG(shortcut)
372 && (!
static_cast<QKeyEvent *>(e)->matches(QKeySequence::Cancel)
373#if defined(Q_OS_ANDROID)
374 ||
static_cast<QKeyEvent *>(e)->key() != Qt::Key_Back
381 return QQuickWindowQmlImpl::event(e);
384void QQuickPopupWindow::windowChanged(QWindow *window)
386 Q_D(QQuickPopupWindow);
387 if (!d->m_popupParentItemWindow.isNull()) {
388 disconnect(d->m_popupParentItemWindow, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
389 disconnect(d->m_popupParentItemWindow, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
392 d->m_popupParentItemWindow = window;
393 connect(window, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
394 connect(window, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
396 d->m_popupParentItemWindow.clear();
398 setTransientParent(window);
401std::optional<QPoint> QQuickPopupWindow::global2Local(
const QPoint &pos)
const
403 Q_D(
const QQuickPopupWindow);
404 QQuickPopup *popup = d->m_popup;
406 QWindow *mainWindow = d->m_popupParentItemWindow;
408 mainWindow = transientParent();
409 if (Q_UNLIKELY((!mainWindow || mainWindow != popup->window())))
412 const QPoint scenePos = mainWindow->mapFromGlobal(pos);
414 return popup->parentItem() ? popup->parentItem()->mapFromScene(scenePos).toPoint() : scenePos;
417void QQuickPopupWindow::parentWindowXChanged(
int newX)
419 const auto popupLocalPos = global2Local({x(), y()});
420 if (Q_UNLIKELY(!popupLocalPos))
422 handlePopupPositionChangeFromWindowSystem({ newX + popupLocalPos->x(), y() });
425void QQuickPopupWindow::parentWindowYChanged(
int newY)
427 const auto popupLocalPos = global2Local({x(), y()});
428 if (Q_UNLIKELY(!popupLocalPos))
430 handlePopupPositionChangeFromWindowSystem({ x(), newY + popupLocalPos->y() });
433void QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem(
const QPoint &pos)
435 Q_D(QQuickPopupWindow);
436 QQuickPopup *popup = d->m_popup;
440 const auto windowPos = global2Local(pos);
441 if (Q_LIKELY(windowPos)) {
442 qCDebug(lcPopupWindow).nospace() <<
"A window system event changed the popup's"
443 <<
" (" << d->m_popup <<
") position to " << *windowPos;
444 QQuickPopupPrivate::get(popup)->setEffectivePosFromWindowPos(*windowPos);
448void QQuickPopupWindow::implicitWidthChanged()
450 Q_D(QQuickPopupWindow);
451 if (
auto popup = d->m_popup) {
452 auto *popupPrivate = QQuickPopupPrivate::get(popup);
458 const QMarginsF insets = popupPrivate->windowInsets();
459 setWidth(qCeil(popup->implicitWidth() + insets.left() + insets.right()));
460 d->m_popupItem->setWidth(popup->implicitWidth());
461 popupPrivate->reposition();
465void QQuickPopupWindow::implicitHeightChanged()
467 Q_D(QQuickPopupWindow);
468 if (
auto popup = d->m_popup) {
469 auto *popupPrivate = QQuickPopupPrivate::get(popup);
470 const QMarginsF insets = popupPrivate->windowInsets();
471 setHeight(qCeil(popup->implicitHeight() + insets.top() + insets.bottom()));
472 d->m_popupItem->setHeight(popup->implicitHeight());
473 popupPrivate->reposition();
477#if QT_CONFIG(wayland)
478QRect QQuickPopupWindow::parentControlGeometry()
const
480 const QQuickItem *parent = popup()->parentItem();
483 const qreal overlap = popup()->property(
"overlap").toReal();
484 QRectF parentItemBoundingRect = parent->boundingRect();
485 const QPointF parentItemMappedPosition = parent->mapToScene(parentItemBoundingRect.topLeft());
486 return { qFloor(parentItemMappedPosition.x() + overlap), qFloor(parentItemMappedPosition.y()),
487 qCeil(qMax<qreal>(qAbs(parentItemBoundingRect.width() - overlap * 2), parentItemBoundingRect.width() / 4)), qCeil(parentItemBoundingRect.height()) };
Combined button and popup list for selecting options.