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 && closePolicy.testAnyFlags(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent)) {
275 if (event->type() != QEvent::NonClientAreaMouseButtonPress && event->type() != QEvent::NonClientAreaMouseButtonDblClick)
276 closePopupAndParentMenus();
279 }
else if (pe->isUpdateEvent()){
280 QQuickWindow *targetWindow =
nullptr;
282 targetWindow = QQuickPopupPrivate::get(targetPopup)->popupWindow;
283 else if (targetMenuBar)
284 targetWindow = targetMenuBar->window();
289 const auto scenePos = pe->point(0).scenePosition();
290 const auto translatedScenePos = targetWindow->mapFromGlobal(globalPos);
291 QMutableEventPoint::setScenePosition(pe->point(0), translatedScenePos);
292 auto *grabber = pe->exclusiveGrabber(pe->point(0));
299 pe->setExclusiveGrabber(pe->point(0),
nullptr);
302 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetWindow;
303 QQuickWindowPrivate::get(targetWindow)->deliveryAgent->event(pe);
306 QMutableEventPoint::setScenePosition(pe->point(0), scenePos);
308 pe->setExclusiveGrabber(pe->point(0), grabber);
309 }
else if (pe->isEndEvent()) {
310 if (!targetPopup && !targetMenuBar && closePolicy.testAnyFlags(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent)) {
313 if (event->type() != QEvent::NonClientAreaMouseButtonRelease)
314 closePopupAndParentMenus();
321 if (QQuickMenu *targetMenu = qobject_cast<QQuickMenu *>(targetPopup)) {
322 qCDebug(lcPopupWindow) <<
"forwarding" << pe <<
"to popup menu:" << targetMenu;
323 QQuickMenuPrivate::get(targetMenu)->handleReleaseWithoutGrab(pe->point(0));
331bool QQuickPopupWindow::event(QEvent *e)
333 Q_D(QQuickPopupWindow);
334#if QT_CONFIG(wayland)
335 if (e->type() == QEvent::PlatformSurface &&
static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
336 if (
auto *waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(handle())) {
337 waylandWindow->setExtendedWindowType(QQuickPopupPrivate::get(d->m_popup)->extendedWindowType);
338 waylandWindow->setParentControlGeometry(parentControlGeometry());
343 if (d->filterPopupSpecialCases(e))
346 if (QQuickPopup *popup = d->m_popup) {
348 if (!popup->hasFocus() && (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease)
349#if QT_CONFIG(shortcut)
350 && (!
static_cast<QKeyEvent *>(e)->matches(QKeySequence::Cancel)
351#if defined(Q_OS_ANDROID)
352 ||
static_cast<QKeyEvent *>(e)->key() != Qt::Key_Back
359 return QQuickWindowQmlImpl::event(e);
362void QQuickPopupWindow::windowChanged(QWindow *window)
364 Q_D(QQuickPopupWindow);
365 if (!d->m_popupParentItemWindow.isNull()) {
366 disconnect(d->m_popupParentItemWindow, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
367 disconnect(d->m_popupParentItemWindow, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
370 d->m_popupParentItemWindow = window;
371 connect(window, &QWindow::xChanged,
this, &QQuickPopupWindow::parentWindowXChanged);
372 connect(window, &QWindow::yChanged,
this, &QQuickPopupWindow::parentWindowYChanged);
374 d->m_popupParentItemWindow.clear();
378std::optional<QPoint> QQuickPopupWindow::global2Local(
const QPoint &pos)
const
380 Q_D(
const QQuickPopupWindow);
381 QQuickPopup *popup = d->m_popup;
383 QWindow *mainWindow = d->m_popupParentItemWindow;
385 mainWindow = transientParent();
386 if (Q_UNLIKELY((!mainWindow || mainWindow != popup->window())))
389 const QPoint scenePos = mainWindow->mapFromGlobal(pos);
391 return popup->parentItem() ? popup->parentItem()->mapFromScene(scenePos).toPoint() : scenePos;
394void QQuickPopupWindow::parentWindowXChanged(
int newX)
396 const auto popupLocalPos = global2Local({x(), y()});
397 if (Q_UNLIKELY(!popupLocalPos))
399 handlePopupPositionChangeFromWindowSystem({ newX + popupLocalPos->x(), y() });
402void QQuickPopupWindow::parentWindowYChanged(
int newY)
404 const auto popupLocalPos = global2Local({x(), y()});
405 if (Q_UNLIKELY(!popupLocalPos))
407 handlePopupPositionChangeFromWindowSystem({ x(), newY + popupLocalPos->y() });
410void QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem(
const QPoint &pos)
412 Q_D(QQuickPopupWindow);
413 QQuickPopup *popup = d->m_popup;
417 const auto windowPos = global2Local(pos);
418 if (Q_LIKELY(windowPos)) {
419 qCDebug(lcPopupWindow).nospace() <<
"A window system event changed the popup's"
420 <<
" (" << d->m_popup <<
") position to " << *windowPos;
421 QQuickPopupPrivate::get(popup)->setEffectivePosFromWindowPos(*windowPos);
425void QQuickPopupWindow::implicitWidthChanged()
427 Q_D(
const QQuickPopupWindow);
428 if (
auto popup = d->m_popup)
429 setWidth(popup->implicitWidth());
432void QQuickPopupWindow::implicitHeightChanged()
434 Q_D(
const QQuickPopupWindow);
435 if (
auto popup = d->m_popup)
436 setHeight(popup->implicitHeight());
439#if QT_CONFIG(wayland)
440QRect QQuickPopupWindow::parentControlGeometry()
const
442 const QQuickItem *parent = popup()->parentItem();
445 const qreal overlap = popup()->property(
"overlap").toReal();
446 QRectF parentItemBoundingRect = parent->boundingRect();
447 const QPointF parentItemMappedPosition = parent->mapToScene(parentItemBoundingRect.topLeft());
448 return { qFloor(parentItemMappedPosition.x() + overlap), qFloor(parentItemMappedPosition.y()),
449 qCeil(qMax<qreal>(qAbs(parentItemBoundingRect.width() - overlap * 2), parentItemBoundingRect.width() / 4)), qCeil(parentItemBoundingRect.height()) };
Combined button and popup list for selecting options.