aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quicktemplates/CMakeLists.txt2
-rw-r--r--src/quicktemplates/qquickdialog.cpp28
-rw-r--r--src/quicktemplates/qquickdialog_p.h2
-rw-r--r--src/quicktemplates/qquickdialog_p_p.h2
-rw-r--r--src/quicktemplates/qquickdrawer.cpp5
-rw-r--r--src/quicktemplates/qquickdrawer_p_p.h2
-rw-r--r--src/quicktemplates/qquickpopup.cpp189
-rw-r--r--src/quicktemplates/qquickpopup_p.h4
-rw-r--r--src/quicktemplates/qquickpopup_p_p.h8
-rw-r--r--src/quicktemplates/qquickpopuppositioner.cpp30
-rw-r--r--src/quicktemplates/qquickpopupwindow.cpp171
-rw-r--r--src/quicktemplates/qquickpopupwindow_p_p.h56
-rw-r--r--src/quicktemplates/qquicktooltip.cpp7
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp3
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/popupCenterIn.qml21
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/popupWindowFocusHandling.qml27
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/popupWithButtonInBackground.qml27
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/reparentingPopup.qml48
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/simplepopup.qml23
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp278
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp1
21 files changed, 878 insertions, 56 deletions
diff --git a/src/quicktemplates/CMakeLists.txt b/src/quicktemplates/CMakeLists.txt
index db49bd9a23..18a128248e 100644
--- a/src/quicktemplates/CMakeLists.txt
+++ b/src/quicktemplates/CMakeLists.txt
@@ -67,6 +67,8 @@ qt_internal_add_qml_module(QuickTemplates2
qquickpopupitem_p_p.h
qquickpopuppositioner.cpp
qquickpopuppositioner_p_p.h
+ qquickpopupwindow.cpp
+ qquickpopupwindow_p_p.h
qquickpresshandler.cpp
qquickpresshandler_p_p.h
qquickprogressbar.cpp qquickprogressbar_p.h
diff --git a/src/quicktemplates/qquickdialog.cpp b/src/quicktemplates/qquickdialog.cpp
index 30daee5f33..7a1c5d513b 100644
--- a/src/quicktemplates/qquickdialog.cpp
+++ b/src/quicktemplates/qquickdialog.cpp
@@ -6,6 +6,7 @@
#include "qquickdialogbuttonbox_p.h"
#include "qquickabstractbutton_p.h"
#include "qquickpopupitem_p_p.h"
+#include "qquickpopupwindow_p_p.h"
QT_BEGIN_NAMESPACE
@@ -164,6 +165,11 @@ void QQuickDialogPrivate::handleClick(QQuickAbstractButton *button)
}
}
+Qt::WindowFlags QQuickDialogPrivate::popupWindowType() const
+{
+ return Qt::Dialog;
+}
+
QQuickDialog::QQuickDialog(QObject *parent)
: QQuickDialog(*(new QQuickDialogPrivate), parent)
{
@@ -176,8 +182,6 @@ QQuickDialog::QQuickDialog(QQuickDialogPrivate &dd, QObject *parent)
// Dialogs should get active focus when opened so that e.g. Cancel closes them.
setFocus(true);
-
- QObject::connect(d->popupItem, &QQuickPopupItem::titleChanged, this, &QQuickDialog::titleChanged);
QObject::connect(d->popupItem, &QQuickPopupItem::headerChanged, this, &QQuickDialog::headerChanged);
QObject::connect(d->popupItem, &QQuickPopupItem::footerChanged, this, &QQuickDialog::footerChanged);
QObject::connect(d->popupItem, &QQuickPopupItem::implicitHeaderWidthChanged, this, &QQuickDialog::implicitHeaderWidthChanged);
@@ -189,7 +193,6 @@ QQuickDialog::QQuickDialog(QQuickDialogPrivate &dd, QObject *parent)
QQuickDialog::~QQuickDialog()
{
Q_D(QQuickDialog);
- QObject::disconnect(d->popupItem, &QQuickPopupItem::titleChanged, this, &QQuickDialog::titleChanged);
QObject::disconnect(d->popupItem, &QQuickPopupItem::headerChanged, this, &QQuickDialog::headerChanged);
QObject::disconnect(d->popupItem, &QQuickPopupItem::footerChanged, this, &QQuickDialog::footerChanged);
QObject::disconnect(d->popupItem, &QQuickPopupItem::implicitHeaderWidthChanged, this, &QQuickDialog::implicitHeaderWidthChanged);
@@ -218,13 +221,22 @@ QQuickDialog::~QQuickDialog()
QString QQuickDialog::title() const
{
Q_D(const QQuickDialog);
- return d->popupItem->title();
+ return d->m_title;
}
void QQuickDialog::setTitle(const QString &title)
{
Q_D(QQuickDialog);
- d->popupItem->setTitle(title);
+ if (d->m_title == title)
+ return;
+ d->m_title = title;
+
+ if (d->popupWindow)
+ d->popupWindow->setTitle(title);
+ else
+ d->popupItem->setTitle(title);
+
+ emit titleChanged();
}
/*!
@@ -488,6 +500,12 @@ qreal QQuickDialog::implicitFooterHeight() const
return d->popupItem->implicitFooterHeight();
}
+void QQuickDialog::setOpacity(qreal opacity)
+{
+ Q_D(QQuickDialog);
+ QQuickPopup::setOpacity(d->popupWindow ? qreal(!qFuzzyIsNull(opacity)) : opacity);
+}
+
/*!
\qmlmethod void QtQuick.Controls::Dialog::accept()
diff --git a/src/quicktemplates/qquickdialog_p.h b/src/quicktemplates/qquickdialog_p.h
index 63fac39f97..c28e687b79 100644
--- a/src/quicktemplates/qquickdialog_p.h
+++ b/src/quicktemplates/qquickdialog_p.h
@@ -74,6 +74,8 @@ public:
qreal implicitFooterWidth() const;
qreal implicitFooterHeight() const;
+ void setOpacity(qreal opacity) override;
+
public Q_SLOTS:
virtual void accept();
virtual void reject();
diff --git a/src/quicktemplates/qquickdialog_p_p.h b/src/quicktemplates/qquickdialog_p_p.h
index dd4c3fa1f0..9b0e5d177a 100644
--- a/src/quicktemplates/qquickdialog_p_p.h
+++ b/src/quicktemplates/qquickdialog_p_p.h
@@ -40,6 +40,8 @@ public:
virtual void handleReject();
virtual void handleClick(QQuickAbstractButton *button);
+ Qt::WindowFlags popupWindowType() const override;
+
int result = 0;
QString title;
QQuickDialogButtonBox *buttonBox = nullptr;
diff --git a/src/quicktemplates/qquickdrawer.cpp b/src/quicktemplates/qquickdrawer.cpp
index f67a8ec76a..e240f8c612 100644
--- a/src/quicktemplates/qquickdrawer.cpp
+++ b/src/quicktemplates/qquickdrawer.cpp
@@ -596,6 +596,11 @@ bool QQuickDrawerPrivate::prepareExitTransition()
return QQuickPopupPrivate::prepareExitTransition();
}
+Qt::WindowFlags QQuickDrawerPrivate::popupWindowType() const
+{
+ return Qt::Widget;
+}
+
bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
{
Q_Q(QQuickDrawer);
diff --git a/src/quicktemplates/qquickdrawer_p_p.h b/src/quicktemplates/qquickdrawer_p_p.h
index 7eae26b0cb..f7757fa1a9 100644
--- a/src/quicktemplates/qquickdrawer_p_p.h
+++ b/src/quicktemplates/qquickdrawer_p_p.h
@@ -54,6 +54,8 @@ public:
bool prepareEnterTransition() override;
bool prepareExitTransition() override;
+ Qt::WindowFlags popupWindowType() const override;
+
bool setEdge(Qt::Edge edge);
Qt::Edge effectiveEdge() const;
bool isWithinDragMargin(const QPointF &point) const;
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index c1c0de46b8..33013caaf4 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -5,6 +5,7 @@
#include "qquickpopup_p_p.h"
#include "qquickpopupanchors_p.h"
#include "qquickpopupitem_p_p.h"
+#include "qquickpopupwindow_p_p.h"
#include "qquickpopuppositioner_p_p.h"
#include "qquickapplicationwindow_p.h"
#include "qquickoverlay_p_p.h"
@@ -19,6 +20,8 @@
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <qpa/qplatformintegration.h>
+#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
@@ -39,8 +42,8 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
used with \l Window or \l ApplicationWindow.
\qml
- import QtQuick.Window 2.2
- import QtQuick.Controls 2.12
+ import QtQuick.Window
+ import QtQuick.Controls
ApplicationWindow {
id: window
@@ -66,10 +69,6 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endqml
- In order to ensure that a popup is displayed above other items in the
- scene, it is recommended to use ApplicationWindow. ApplicationWindow also
- provides background dimming effects.
-
Popup does not provide a layout of its own, but requires you to position
its contents, for instance by creating a \l RowLayout or a \l ColumnLayout.
@@ -121,6 +120,25 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endcode
+ \section1 Popup Windows
+
+ Popup can behave in two different ways. Depending on the platform,
+ and whether the \l Qt::AA_DontUsePopupWindows application attribute is set or not.
+
+ By default, on desktop systems, the popup will create a special popup window,
+ which contains the \l contentItem and \l background items.
+
+ \section1 Popup Items
+
+ If the \l Qt::AA_DontUsePopupWindows application attribute is set,
+ or the platform doesn't support multiple windows,
+ the popup will instead create a special item, which gets parented to the
+ \l{Overlay::overlay}{overlay} in the scene of the existing window.
+
+ In order to ensure that a popup is displayed above other items in the
+ scene, it is recommended to use ApplicationWindow. ApplicationWindow also
+ provides background dimming effects.
+
\section1 Popup Sizing
If only a single item is used within a Popup, it will resize to fit the
@@ -172,7 +190,7 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endcode
- \note The popup's \l{contentItem}{content item} gets parented to the
+ \note When using \l {Popup Items}, the popup's \l{contentItem}{content item} gets parented to the
\l{Overlay::overlay}{overlay}, and does not live within the popup's parent.
Because of that, a \l{Item::scale}{scale} applied to the tree in which
the popup lives does not apply to the visual popup. To make the popup
@@ -221,7 +239,8 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
\section1 Showing Non-Child Items in Front of Popup
- Popup sets its contentItem's
+
+ In cases where \l {Popup Windows} are not being used, Popup sets its contentItem's
\l{qtquick-visualcanvas-visualparent.html}{visual parent}
to be the window's \l{Overlay::overlay}{overlay}, in order to ensure that
the popup appears in front of everything else in the scene.
@@ -372,6 +391,18 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
\sa closed()
*/
+QQuickItem *QQuickPopup::findParentItem() const
+{
+ QObject *obj = parent();
+ while (obj) {
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj);
+ if (item)
+ return item;
+ obj = obj->parent();
+ }
+ return nullptr;
+}
+
const QQuickPopup::ClosePolicy QQuickPopupPrivate::DefaultClosePolicy = QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutside;
QQuickPopupPrivate::QQuickPopupPrivate()
@@ -384,7 +415,6 @@ void QQuickPopupPrivate::init()
Q_Q(QQuickPopup);
popupItem = new QQuickPopupItem(q);
popupItem->setVisible(false);
- q->setParentItem(qobject_cast<QQuickItem *>(parent));
QObject::connect(popupItem, &QQuickControl::paddingChanged, q, &QQuickPopup::paddingChanged);
QObject::connect(popupItem, &QQuickControl::backgroundChanged, q, &QQuickPopup::backgroundChanged);
QObject::connect(popupItem, &QQuickControl::contentItemChanged, q, &QQuickPopup::contentItemChanged);
@@ -587,37 +617,17 @@ bool QQuickPopupPrivate::prepareEnterTransition()
return false;
if (transitionState != EnterTransition) {
- QQuickOverlay *overlay = QQuickOverlay::overlay(window);
- const auto popupStack = QQuickOverlayPrivate::get(overlay)->stackingOrderPopups();
- popupItem->setParentItem(overlay);
- // if there is a stack of popups, and the current top popup item belongs to an
- // ancestor of this popup, then make sure that this popup's item is at the top
- // of the stack.
- const QQuickPopup *topPopup = popupStack.isEmpty() ? nullptr : popupStack.first();
- const QObject *ancestor = q;
- while (ancestor && topPopup) {
- if (ancestor == topPopup)
- break;
- ancestor = ancestor->parent();
- }
- if (topPopup && topPopup != q && ancestor) {
- QQuickItem *topPopupItem = popupStack.first()->popupItem();
- popupItem->stackAfter(topPopupItem);
- // If the popup doesn't have an explicit z value set, set it to be at least as
- // high as the current top popup item so that later opened popups are on top.
- if (!hasZ)
- popupItem->setZ(qMax(topPopupItem->z(), popupItem->z()));
- }
+ visible = true;
+ adjustPopupItemParentAndWindow();
if (dim)
createOverlay();
showDimmer();
emit q->aboutToShow();
- visible = true;
transitionState = EnterTransition;
- popupItem->setVisible(true);
getPositioner()->setParentItem(parentItem);
emit q->visibleChanged();
+ QQuickOverlay *overlay = QQuickOverlay::overlay(window);
auto *overlayPrivate = QQuickOverlayPrivate::get(overlay);
if (overlayPrivate->lastActiveFocusItem.isNull())
overlayPrivate->lastActiveFocusItem = window->activeFocusItem();
@@ -663,7 +673,7 @@ void QQuickPopupPrivate::finalizeEnterTransition()
{
Q_Q(QQuickPopup);
transitionState = NoTransition;
- getPositioner()->reposition();
+ reposition();
emit q->openedChanged();
opened();
}
@@ -672,7 +682,7 @@ void QQuickPopupPrivate::finalizeExitTransition()
{
Q_Q(QQuickPopup);
getPositioner()->setParentItem(nullptr);
- if (popupItem) {
+ if (popupItem && !popupWindow) {
popupItem->setParentItem(nullptr);
popupItem->setVisible(false);
}
@@ -707,8 +717,8 @@ void QQuickPopupPrivate::finalizeExitTransition()
overlayPrivate->lastActiveFocusItem = nullptr;
}
}
-
visible = false;
+ adjustPopupItemParentAndWindow();
transitionState = NoTransition;
hadActiveFocusBeforeExitTransition = false;
emit q->visibleChanged();
@@ -725,6 +735,11 @@ void QQuickPopupPrivate::opened()
emit q->opened();
}
+Qt::WindowFlags QQuickPopupPrivate::popupWindowType() const
+{
+ return Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint;
+}
+
QMarginsF QQuickPopupPrivate::getMargins() const
{
Q_Q(const QQuickPopup);
@@ -880,6 +895,69 @@ QPalette QQuickPopupPrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::System);
}
+bool QQuickPopupPrivate::usePopupWindow() const
+{
+ // TODO: fix later
+ return false;
+ // return QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::Capability::MultipleWindows)
+ // && !QCoreApplication::testAttribute(Qt::AA_DontUsePopupWindows)
+ // && popupWindowType() != Qt::Widget; // We use Qt::Widget here, to allow some popup derived types, like drawer, to opt out of using separate windows.
+}
+
+void QQuickPopupPrivate::adjustPopupItemParentAndWindow()
+{
+ Q_Q(QQuickPopup);
+ QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+
+ if (visible && popupWindowDirty) {
+ popupItem->setParentItem(overlay);
+ if (popupWindow) {
+ popupWindow->deleteLater();
+ popupWindow = nullptr;
+ }
+ popupWindowDirty = false;
+ }
+
+ if (usePopupWindow()) {
+ if (!popupWindow) {
+ popupWindow = new QQuickPopupWindow(q, window);
+ popupWindow->setModality(modal ? Qt::ApplicationModal : Qt::NonModal);
+ popupItem->resetTitle();
+ popupWindow->setTitle(m_title);
+ popupItem->setParentItem(popupWindow->contentItem());
+ popupItem->forceActiveFocus(Qt::PopupFocusReason);
+ }
+ popupItem->setVisible(true);
+ popupWindow->setVisible(visible);
+ } else {
+ if (visible) {
+ popupItem->setParentItem(overlay);
+ const auto popupStack = QQuickOverlayPrivate::get(overlay)->stackingOrderPopups();
+ // if there is a stack of popups, and the current top popup item belongs to an
+ // ancestor of this popup, then make sure that this popup's item is at the top
+ // of the stack.
+ const QQuickPopup *topPopup = popupStack.isEmpty() ? nullptr : popupStack.first();
+ const QObject *ancestor = q;
+ while (ancestor && topPopup) {
+ if (ancestor == topPopup)
+ break;
+ ancestor = ancestor->parent();
+ }
+ if (topPopup && topPopup != q && ancestor) {
+ QQuickItem *topPopupItem = popupStack.first()->popupItem();
+ popupItem->stackAfter(topPopupItem);
+ // If the popup doesn't have an explicit z value set, set it to be at least as
+ // high as the current top popup item so that later opened popups are on top.
+ if (!hasZ)
+ popupItem->setZ(qMax(topPopupItem->z(), popupItem->z()));
+ }
+ }
+
+ popupItem->setTitle(m_title);
+ popupItem->setVisible(visible);
+ }
+}
+
static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQuickItem *parent)
{
QQuickItem *item = nullptr;
@@ -1190,6 +1268,9 @@ void QQuickPopup::setPosition(const QPointF &pos)
of an already open popup, then it will be stacked on top of its parent.
This ensures that children are never hidden under their parents.
+ If the popup has its own window, the z-value will determine the window
+ stacking order instead.
+
The default z-value is \c 0.
\sa x, y
@@ -1204,9 +1285,13 @@ void QQuickPopup::setZ(qreal z)
{
Q_D(QQuickPopup);
d->hasZ = true;
- if (qFuzzyCompare(z, d->popupItem->z()))
+ bool previousZ = d->popupWindow ? d->popupWindow->z() : d->popupItem->z();
+ if (qFuzzyCompare(z, previousZ))
return;
- d->popupItem->setZ(z);
+ if (d->popupWindow)
+ d->popupWindow->setZ(z);
+ else
+ d->popupItem->setZ(z);
emit zChanged();
}
@@ -1232,7 +1317,16 @@ void QQuickPopup::setWidth(qreal width)
{
Q_D(QQuickPopup);
d->hasWidth = true;
- d->popupItem->setWidth(width);
+
+ // QQuickPopupWindow::setWidth() triggers a window resize event.
+ // This will cause QQuickPopupWindow::resizeEvent() to resize
+ // the popupItem. QQuickPopupItem::geometryChanged() calls QQuickPopup::geometryChange(),
+ // which emits widthChanged().
+
+ if (d->popupWindow)
+ d->popupWindow->setWidth(width);
+ else
+ d->popupItem->setWidth(width);
}
void QQuickPopup::resetWidth()
@@ -1262,7 +1356,16 @@ void QQuickPopup::setHeight(qreal height)
{
Q_D(QQuickPopup);
d->hasHeight = true;
- d->popupItem->setHeight(height);
+
+ // QQuickPopupWindow::setHeight() triggers a window resize event.
+ // This will cause QQuickPopupWindow::resizeEvent() to resize
+ // the popupItem. QQuickPopupItem::geometryChanged() calls QQuickPopup::geometryChange(),
+ // which emits heightChanged().
+
+ if (d->popupWindow)
+ d->popupWindow->setHeight(height);
+ else
+ d->popupItem->setHeight(height);
}
void QQuickPopup::resetHeight()
@@ -1871,7 +1974,7 @@ void QQuickPopup::resetParentItem()
if (QQuickWindow *window = qobject_cast<QQuickWindow *>(parent()))
setParentItem(window->contentItem());
else
- setParentItem(qobject_cast<QQuickItem *>(parent()));
+ setParentItem(findParentItem());
}
/*!
@@ -2002,17 +2105,18 @@ QQmlListProperty<QQuickItem> QQuickPopupPrivate::contentChildren()
\qmlproperty bool QtQuick.Controls::Popup::clip
This property holds whether clipping is enabled. The default value is \c false.
+ Clipping only works when the popup isn't in its own window.
*/
bool QQuickPopup::clip() const
{
Q_D(const QQuickPopup);
- return d->popupItem->clip();
+ return d->popupItem->clip() && !d->usePopupWindow();
}
void QQuickPopup::setClip(bool clip)
{
Q_D(QQuickPopup);
- if (clip == d->popupItem->clip())
+ if (clip == d->popupItem->clip() || d->usePopupWindow())
return;
d->popupItem->setClip(clip);
emit clipChanged();
@@ -2092,6 +2196,7 @@ void QQuickPopup::setModal(bool modal)
if (d->modal == modal)
return;
d->modal = modal;
+ d->popupWindowDirty = true;
if (d->complete && d->visible)
d->toggleOverlay();
emit modalChanged();
diff --git a/src/quicktemplates/qquickpopup_p.h b/src/quicktemplates/qquickpopup_p.h
index abd2a1bdf7..f81ec5027d 100644
--- a/src/quicktemplates/qquickpopup_p.h
+++ b/src/quicktemplates/qquickpopup_p.h
@@ -226,7 +226,7 @@ public:
virtual void setVisible(bool visible);
qreal opacity() const;
- void setOpacity(qreal opacity);
+ virtual void setOpacity(qreal opacity);
qreal scale() const;
void setScale(qreal scale);
@@ -433,6 +433,8 @@ protected:
bool setAccessibleProperty(const char *propertyName, const QVariant &value);
private:
+ QQuickItem *findParentItem() const;
+
Q_DISABLE_COPY(QQuickPopup)
Q_DECLARE_PRIVATE(QQuickPopup)
friend class QQuickPopupItem;
diff --git a/src/quicktemplates/qquickpopup_p_p.h b/src/quicktemplates/qquickpopup_p_p.h
index 5572dd87d1..556f3b6154 100644
--- a/src/quicktemplates/qquickpopup_p_p.h
+++ b/src/quicktemplates/qquickpopup_p_p.h
@@ -34,6 +34,7 @@ class QQuickTransitionManager;
class QQuickPopup;
class QQuickPopupAnchors;
class QQuickPopupItem;
+class QQuickPopupWindow;
class QQuickPopupPrivate;
class QQuickPopupPositioner;
@@ -94,6 +95,8 @@ public:
void reposition();
+ bool usePopupWindow() const;
+ void adjustPopupItemParentAndWindow();
void createOverlay();
void destroyDimmer();
void toggleOverlay();
@@ -109,6 +112,8 @@ public:
virtual void opened();
+ virtual Qt::WindowFlags popupWindowType() const;
+
QMarginsF getMargins() const;
void setTopMargin(qreal value, bool reset = false);
@@ -157,6 +162,7 @@ public:
bool outsideParentPressed = false;
bool inDestructor = false;
bool relaxEdgeConstraint = false;
+ bool popupWindowDirty = false;
int touchId = -1;
qreal x = 0;
qreal y = 0;
@@ -176,6 +182,7 @@ public:
QQuickTransition *enter = nullptr;
QQuickTransition *exit = nullptr;
QQuickPopupItem *popupItem = nullptr;
+ QQuickPopupWindow *popupWindow = nullptr;
QQuickPopupPositioner *positioner = nullptr;
QList<QQuickStateAction> enterActions;
QList<QQuickStateAction> exitActions;
@@ -184,6 +191,7 @@ public:
qreal explicitDimmerOpacity = 0;
qreal prevOpacity = 0;
qreal prevScale = 0;
+ QString m_title;
friend class QQuickPopupTransitionManager;
};
diff --git a/src/quicktemplates/qquickpopuppositioner.cpp b/src/quicktemplates/qquickpopuppositioner.cpp
index f8113a5526..8c247290b3 100644
--- a/src/quicktemplates/qquickpopuppositioner.cpp
+++ b/src/quicktemplates/qquickpopuppositioner.cpp
@@ -5,6 +5,7 @@
#include "qquickpopuppositioner_p_p.h"
#include "qquickpopupanchors_p.h"
#include "qquickpopupitem_p_p.h"
+#include "qquickpopupwindow_p_p.h"
#include "qquickpopup_p_p.h"
#include <QtCore/qloggingcategory.h>
@@ -72,7 +73,32 @@ void QQuickPopupPositioner::setParentItem(QQuickItem *parent)
void QQuickPopupPositioner::reposition()
{
+ if (auto p = QQuickPopupPrivate::get(popup()); p->usePopupWindow()) {
+ if (!p->popupWindow || !p->parentItem) {
+ p->effectiveX = p->x;
+ p->effectiveY = p->y;
+ return;
+ }
+
+ const QQuickItem *centerInParent = p->anchors ? p->getAnchors()->centerIn() : nullptr;
+ const QQuickOverlay *centerInOverlay = qobject_cast<const QQuickOverlay *>(centerInParent);
+ QPoint pos(p->x, p->y);
+
+ if (centerInParent == p->parentItem || centerInOverlay) {
+ pos = centerInOverlay ? QPoint(qRound(centerInOverlay->width() / 2.0), qRound(centerInOverlay->height() / 2.0))
+ : QPoint(qRound(p->parentItem->width() / 2.0), qRound(p->parentItem->height() / 2.0));
+ pos -= QPoint(qRound(p->popupItem->width() / 2.0), qRound(p->popupItem->height() / 2.0));
+
+ } else if (centerInParent)
+ qmlWarning(popup()) << "Popup can only be centered within its immediate parent or Overlay.overlay";
+
+ const QPointF globalCoords = p->parentItem->mapToGlobal(pos.x(), pos.y());
+ p->popupWindow->setPosition(globalCoords.x(), globalCoords.y());
+ return;
+ }
+
QQuickItem *popupItem = m_popup->popupItem();
+
if (!popupItem->isVisible())
return;
@@ -96,8 +122,7 @@ void QQuickPopupPositioner::reposition()
const QQuickOverlay *centerInOverlay = qobject_cast<const QQuickOverlay*>(centerInParent);
QRectF rect(!centerInParent ? p->allowHorizontalMove ? p->x : popupItem->x() : 0,
!centerInParent ? p->allowVerticalMove ? p->y : popupItem->y() : 0,
- !p->hasWidth && iw > 0 ? iw : w,
- !p->hasHeight && ih > 0 ? ih : h);
+ !p->hasWidth && iw > 0 ? iw : w, !p->hasHeight && ih > 0 ? ih : h);
bool relaxEdgeConstraint = p->relaxEdgeConstraint;
if (m_parentItem) {
// m_parentItem is the parent that the popup should open in,
@@ -236,6 +261,7 @@ void QQuickPopupPositioner::reposition()
// so we need to map its top left back to item coordinates.
// However, if centering within the overlay, the coordinates will be relative
// to the window, so we don't need to do anything.
+ // The same applies to popups that are in their own dedicated window.
const QPointF effectivePos = m_parentItem && !centerInOverlay ? m_parentItem->mapFromScene(rect.topLeft()) : rect.topLeft();
if (!qFuzzyCompare(p->effectiveX, effectivePos.x())) {
p->effectiveX = effectivePos.x();
diff --git a/src/quicktemplates/qquickpopupwindow.cpp b/src/quicktemplates/qquickpopupwindow.cpp
new file mode 100644
index 0000000000..c61be40f94
--- /dev/null
+++ b/src/quicktemplates/qquickpopupwindow.cpp
@@ -0,0 +1,171 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickpopupwindow_p_p.h"
+#include "qquickcombobox_p.h"
+#include "qquickpopup_p.h"
+#include "qquickpopup_p_p.h"
+#include "qquickpopupitem_p_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickwindowmodule_p.h>
+#include <QtQuick/private/qquickwindowmodule_p_p.h>
+#include <qpa/qplatformwindow_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPopupWindow, "qt.quick.controls.popup.window")
+
+class QQuickPopupWindowPrivate : public QQuickWindowQmlImplPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPopupWindow)
+
+public:
+ QPointer<QQuickItem> m_popupItem;
+ QPointer<QQuickPopup> m_popup;
+};
+
+QQuickPopupWindow::QQuickPopupWindow(QQuickPopup *popup, QWindow *parent)
+ : QQuickWindowQmlImpl(*(new QQuickPopupWindowPrivate), nullptr)
+{
+ Q_D(QQuickPopupWindow);
+
+ d->m_popup = popup;
+ d->m_popupItem = popup->popupItem();
+ setTransientParent(parent);
+
+ connect(d->m_popup, &QQuickPopup::windowChanged, this, &QQuickPopupWindow::windowChanged);
+ connect(d->m_popup, &QQuickPopup::implicitWidthChanged, this, &QQuickPopupWindow::implicitWidthChanged);
+ connect(d->m_popup, &QQuickPopup::implicitHeightChanged, this, &QQuickPopupWindow::implicitHeightChanged);
+ connect(d->m_popup->window(), &QWindow::xChanged, this, &QQuickPopupWindow::parentWindowXChanged);
+ connect(d->m_popup->window(), &QWindow::yChanged, this, &QQuickPopupWindow::parentWindowYChanged);
+
+ setWidth(d->m_popupItem->implicitWidth());
+ setHeight(d->m_popupItem->implicitHeight());
+
+ const auto flags = QQuickPopupPrivate::get(popup)->popupWindowType();
+
+ // For popup windows, we'll need to draw everything, in order to have enough control over the styling.
+ if (flags & Qt::Popup)
+ setColor(QColorConstants::Transparent);
+
+ setFlags(flags);
+
+ qCDebug(lcPopupWindow) << "Created popup window with flags: " << flags;
+}
+
+QQuickPopupWindow::~QQuickPopupWindow()
+{
+ Q_D(QQuickPopupWindow);
+ disconnect(d->m_popup, &QQuickPopup::windowChanged, this, &QQuickPopupWindow::windowChanged);
+ disconnect(d->m_popup, &QQuickPopup::implicitWidthChanged, this, &QQuickPopupWindow::implicitWidthChanged);
+ disconnect(d->m_popup, &QQuickPopup::implicitHeightChanged, this, &QQuickPopupWindow::implicitHeightChanged);
+ disconnect(d->m_popup->window(), &QWindow::xChanged, this, &QQuickPopupWindow::parentWindowXChanged);
+ disconnect(d->m_popup->window(), &QWindow::yChanged, this, &QQuickPopupWindow::parentWindowYChanged);
+}
+
+QQuickPopup *QQuickPopupWindow::popup() const
+{
+ Q_D(const QQuickPopupWindow);
+ return d->m_popup;
+}
+
+void QQuickPopupWindow::hideEvent(QHideEvent *e)
+{
+ Q_D(QQuickPopupWindow);
+ QQuickWindow::hideEvent(e);
+ if (QQuickPopup *popup = d->m_popup) {
+ QQuickPopupPrivate::get(popup)->visible = false;
+ emit popup->visibleChanged();
+ }
+}
+
+void QQuickPopupWindow::moveEvent(QMoveEvent *e)
+{
+ handlePopupPositionChangeFromWindowSystem(e->pos());
+}
+
+void QQuickPopupWindow::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QQuickPopupWindow);
+ QQuickWindowQmlImpl::resizeEvent(e);
+
+ if (!d->m_popupItem)
+ return;
+
+ qCDebug(lcPopupWindow) << "A window system event changed the popup's size to be " << e->size();
+ d->m_popupItem->setWidth(e->size().width());
+ d->m_popupItem->setHeight(e->size().height());
+}
+
+void QQuickPopupWindow::windowChanged(QWindow *window)
+{
+ if (window) {
+ connect(window, &QWindow::xChanged, this, &QQuickPopupWindow::parentWindowXChanged);
+ connect(window, &QWindow::yChanged, this, &QQuickPopupWindow::parentWindowYChanged);
+ }
+}
+
+QPoint QQuickPopupWindow::global2Local(const QPoint &pos) const
+{
+ Q_D(const QQuickPopupWindow);
+ QQuickPopup *popup = d->m_popup;
+ Q_ASSERT(popup);
+ const QPoint scenePos = popup->window()->mapFromGlobal(pos);
+ // Popup's coordinates are relative to the nearest parent item.
+ return popup->parentItem() ? popup->parentItem()->mapFromScene(scenePos).toPoint() : scenePos;
+}
+
+void QQuickPopupWindow::parentWindowXChanged(int newX)
+{
+ const auto popupLocalPos = global2Local({x(), y()});
+ handlePopupPositionChangeFromWindowSystem({newX + popupLocalPos.x(), y()});
+}
+
+void QQuickPopupWindow::parentWindowYChanged(int newY)
+{
+ const auto popupLocalPos = global2Local({x(), y()});
+ handlePopupPositionChangeFromWindowSystem({x(), newY + popupLocalPos.y()});
+}
+
+void QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem(const QPoint &pos)
+{
+ Q_D(QQuickPopupWindow);
+ QQuickPopup *popup = d->m_popup;
+ if (!popup)
+ return;
+ QQuickPopupPrivate *popupPrivate = QQuickPopupPrivate::get(popup);
+
+ const auto localPos = global2Local(pos);
+
+ const qreal oldX = popup->x();
+ const qreal oldY = popup->y();
+
+ qCDebug(lcPopupWindow) << "A window system event changed the popup's position to be " << localPos;
+
+ popupPrivate->x = popupPrivate->effectiveX = localPos.x();
+ popupPrivate->y = popupPrivate->effectiveY = localPos.y();
+
+ if (!qFuzzyCompare(oldX, localPos.x()))
+ emit popup->xChanged();
+ if (!qFuzzyCompare(oldY, localPos.y()))
+ emit popup->yChanged();
+}
+
+void QQuickPopupWindow::implicitWidthChanged()
+{
+ Q_D(const QQuickPopupWindow);
+ if (auto popup = d->m_popup)
+ setWidth(popup->implicitWidth());
+}
+
+void QQuickPopupWindow::implicitHeightChanged()
+{
+ Q_D(const QQuickPopupWindow);
+ if (auto popup = d->m_popup)
+ setHeight(popup->implicitHeight());
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quicktemplates/qquickpopupwindow_p_p.h b/src/quicktemplates/qquickpopupwindow_p_p.h
new file mode 100644
index 0000000000..d4d4adda61
--- /dev/null
+++ b/src/quicktemplates/qquickpopupwindow_p_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPOPUPWINDOW_P_P_H
+#define QQUICKPOPUPWINDOW_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qquickwindowmodule_p.h>
+#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPopup;
+class QQuickPopupWindowPrivate;
+
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupWindow : public QQuickWindowQmlImpl
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ explicit QQuickPopupWindow(QQuickPopup *popup, QWindow *parent = nullptr);
+ ~QQuickPopupWindow();
+ QQuickPopup *popup() const;
+
+protected:
+ void hideEvent(QHideEvent *e) override;
+ void moveEvent(QMoveEvent *e) override;
+ void resizeEvent(QResizeEvent *e) override;
+
+private:
+ void windowChanged(QWindow *window);
+ QPoint global2Local(const QPoint& pos) const;
+ void parentWindowXChanged(int newX);
+ void parentWindowYChanged(int newY);
+ void handlePopupPositionChangeFromWindowSystem(const QPoint &pos);
+ void implicitWidthChanged();
+ void implicitHeightChanged();
+
+ Q_DISABLE_COPY(QQuickPopupWindow)
+ Q_DECLARE_PRIVATE(QQuickPopupWindow)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOPUPWINDOW_P_P_H
diff --git a/src/quicktemplates/qquicktooltip.cpp b/src/quicktemplates/qquicktooltip.cpp
index 2a78e4d344..0493cd5f4e 100644
--- a/src/quicktemplates/qquicktooltip.cpp
+++ b/src/quicktemplates/qquicktooltip.cpp
@@ -102,6 +102,8 @@ public:
void opened() override;
+ Qt::WindowFlags popupWindowType() const override;
+
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::ToolTip); }
int delay = 0;
@@ -141,6 +143,11 @@ void QQuickToolTipPrivate::opened()
startTimeout();
}
+Qt::WindowFlags QQuickToolTipPrivate::popupWindowType() const
+{
+ return Qt::ToolTip;
+}
+
QQuickToolTip::QQuickToolTip(QQuickItem *parent)
: QQuickPopup(*(new QQuickToolTipPrivate), parent)
{
diff --git a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
index 0ef48c2fd1..9cd806f05e 100644
--- a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
@@ -30,7 +30,7 @@ public:
tst_qquickmenubar();
private slots:
- void init();
+ void init() override;
void delegate();
void mouse_data();
void mouse();
@@ -98,7 +98,6 @@ void tst_qquickmenubar::init()
// Note that some tests will set this property to 'true', which
// is why we need to set it back to 'false' here.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false);
- QCoreApplication::setAttribute(Qt::AA_DontUsePopupWindows, false);
}
void tst_qquickmenubar::delegate()
diff --git a/tests/auto/quickcontrols/qquickpopup/data/popupCenterIn.qml b/tests/auto/quickcontrols/qquickpopup/data/popupCenterIn.qml
new file mode 100644
index 0000000000..969cda091a
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/popupCenterIn.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 1080
+ height: 720
+
+ property alias popup: simplepopup
+
+ Popup {
+ id: simplepopup
+ anchors.centerIn: parent
+
+ Text {
+ text: "I am a centered popup"
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/popupWindowFocusHandling.qml b/tests/auto/quickcontrols/qquickpopup/data/popupWindowFocusHandling.qml
new file mode 100644
index 0000000000..60033d34a3
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/popupWindowFocusHandling.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 400
+ height: 400
+
+ property alias popup: simplepopup
+ property alias textField1: outerTextField
+ property alias textField2: innerTextField
+
+ TextField {
+ id: outerTextField
+ focus: true
+ }
+
+ Popup {
+ id: simplepopup
+ TextField {
+ id: innerTextField
+ focus: true
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/popupWithButtonInBackground.qml b/tests/auto/quickcontrols/qquickpopup/data/popupWithButtonInBackground.qml
new file mode 100644
index 0000000000..d54c3a5d88
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/popupWithButtonInBackground.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 1080
+ height: 720
+
+ property alias popup: simplepopup
+
+ Button {
+ text: "Button"
+ }
+
+ Popup {
+ id: simplepopup
+
+ x: 50
+ y: 50
+
+ Text {
+ text: "I am a very interesting popup"
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/reparentingPopup.qml b/tests/auto/quickcontrols/qquickpopup/data/reparentingPopup.qml
new file mode 100644
index 0000000000..d92e6893dd
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/reparentingPopup.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 400
+ height: 400
+
+ property alias popup: simplepopup
+ property alias rectangle1: item1
+ property alias rectangle2: item2
+ property alias rectangle3: item3
+
+ Popup {
+ id: simplepopup
+ visible: true
+ x: 10
+ y: 10
+ width: 200
+ height: 200
+ }
+
+ Rectangle {
+ id: item1
+ color: "red"
+ width: 200
+ height: 200
+ }
+
+ Rectangle {
+ id: item2
+ color: "green"
+ x: 0
+ y: 200
+ width: parent.width
+ height: 200
+ Rectangle {
+ id: item3
+ color: "blue"
+ x: 200
+ y: 0
+ width: 200
+ height: item2.height
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/simplepopup.qml b/tests/auto/quickcontrols/qquickpopup/data/simplepopup.qml
new file mode 100644
index 0000000000..ca6daefcc6
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/simplepopup.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 1080
+ height: 720
+
+ property alias popup: simplepopup
+
+ Popup {
+ id: simplepopup
+
+ x: 50
+ y: 50
+
+ Text {
+ text: "I am a very interesting popup"
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index 3743fbf713..8b9e8aa976 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -16,18 +16,21 @@
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTemplates2/private/qquickcombobox_p.h>
#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickdrawer_p.h>
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
#include <QtQuickTemplates2/private/qquickoverlay_p_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
+#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupanchors_p.h>
#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
-#include <QtQuickTemplates2/private/qquickbutton_p.h>
+#include <QtQuickTemplates2/private/qquickpopupwindow_p_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
#include <QtQuickTemplates2/private/qquickstackview_p.h>
-#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
-#include <QtQuickTemplates2/private/qquickdrawer_p.h>
+#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquicktextedit_p.h>
#include <QtQuick/private/qquickdroparea_p.h>
@@ -111,6 +114,13 @@ private slots:
void fadeDimmer();
void noDimmer();
+ void popupWindowPositioning();
+ void popupWindowAnchorsCenterIn_data();
+ void popupWindowAnchorsCenterIn();
+ void popupWindowModality();
+ void popupWindowClosesOnParentWindowClosing();
+ void popupWindowChangingParent();
+
private:
QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
@@ -2367,6 +2377,268 @@ void tst_QQuickPopup::noDimmer()
QTRY_VERIFY(!drawer->isModal());
}
+#define VERIFY_LOCAL_POS(POPUP, EXPECTED) \
+ QTRY_COMPARE_LE(qAbs(POPUP->x() - qreal(EXPECTED.x())), 1); \
+ QCOMPARE_LE(qAbs(POPUP->position().x() - qreal(EXPECTED.x())), 1); \
+ QCOMPARE_LE(qAbs(POPUP->y() - qreal(EXPECTED.y())), 1); \
+ QCOMPARE_LE(qAbs(POPUP->position().y() - qreal(EXPECTED.y())), 1)
+
+#define VERIFY_GLOBAL_POS(FROM, POPUPWINDOW, EXPECTED) \
+ do { \
+ const auto expectedGlobalPos = FROM->mapToGlobal(EXPECTED.x(), EXPECTED.y()); \
+ const auto actualGlobalPos = POPUPWINDOW->position(); \
+ QTRY_COMPARE_LE(qAbs(actualGlobalPos.x() - qFloor(expectedGlobalPos.x())), 1); \
+ QCOMPARE_LE(qAbs(actualGlobalPos.y() - qFloor(expectedGlobalPos.y())), 1); \
+ } while (false)
+
+void tst_QQuickPopup::popupWindowPositioning()
+{
+ QSKIP("Enable test later");
+ QQuickApplicationHelper helper(this, "simplepopup.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ QVERIFY(popup);
+ auto *popupPrivate = QQuickPopupPrivate::get(popup);
+ QVERIFY(popupPrivate);
+
+ if (!popupPrivate->usePopupWindow())
+ QSKIP("The platform doesn't support native popup windows. Skipping test.");
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+
+ QSignalSpy xSpy(popup, SIGNAL(xChanged()));
+ QSignalSpy ySpy(popup, SIGNAL(yChanged()));
+
+ auto *popupWindow = popupPrivate->popupWindow;
+ QVERIFY(popupWindow);
+
+ // x and y properties should be 50 initially
+ const QPoint initialPos(50, 50);
+
+ VERIFY_GLOBAL_POS(popup->parentItem(), popupWindow, initialPos);
+ VERIFY_LOCAL_POS(popup, initialPos);
+
+ // Move popup via QQuickPopup API
+ const QPoint secondPosition(100, 100);
+ popup->setPosition(secondPosition.toPointF());
+
+ QTRY_COMPARE(xSpy.count(), 1);
+ QCOMPARE(ySpy.count(), 1);
+
+ VERIFY_GLOBAL_POS(popup->parentItem(), popupWindow, secondPosition);
+ VERIFY_LOCAL_POS(popup, secondPosition);
+
+ // Move popup via QWindow API (which uses global coordinates)
+ const QPoint thirdPosition(150, 150);
+ popupWindow->setPosition(popup->parentItem()->mapToGlobal(thirdPosition.x(), thirdPosition.y()).toPoint());
+
+ QTRY_COMPARE(xSpy.count(), 2);
+ QCOMPARE(ySpy.count(), 2);
+
+ VERIFY_GLOBAL_POS(popup->parentItem(), popupWindow, thirdPosition);
+ VERIFY_LOCAL_POS(popup, thirdPosition);
+
+ // Moving parent window should change the popups position (because it's stationary, but x and y are relative coordinates)
+ const QPoint movement(30, 30);
+ const QPoint oldPos = window->position();
+ window->setPosition(oldPos + movement);
+
+ // TODO: Figure out these signals are emitted twice
+ // QTRY_COMPARE(xSpy.count(), 3);
+ // QCOMPARE(ySpy.count(), 3);
+
+ VERIFY_GLOBAL_POS(popup->parentItem(), popupWindow, (thirdPosition - movement));
+}
+
+void tst_QQuickPopup::popupWindowAnchorsCenterIn_data()
+{
+ QTest::addColumn<bool>("centerInParent");
+ QTest::newRow("parent") << true;
+ QTest::newRow("overlay") << false;
+}
+
+void tst_QQuickPopup::popupWindowAnchorsCenterIn()
+{
+ QSKIP("Enable test later");
+ QFETCH(bool, centerInParent);
+
+ QQuickApplicationHelper helper(this, "popupCenterIn.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ QVERIFY(popup);
+ auto *popupPrivate = QQuickPopupPrivate::get(popup);
+ QVERIFY(popupPrivate);
+
+ if (!popupPrivate->usePopupWindow())
+ QSKIP("The platform doesn't support native popup windows. Skipping test.");
+
+ popupPrivate->getAnchors()->setCenterIn(centerInParent ? window->contentItem() : QQuickOverlay::overlay(window));
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+
+ auto *popupWindow = popupPrivate->popupWindow;
+ QVERIFY(popupWindow);
+
+ const QPoint centeredPosition(qFloor(window->width() / 2 - popupWindow->width() / 2), qFloor(window->height() / 2 - popupWindow->height() / 2));
+
+ VERIFY_GLOBAL_POS(popup->parentItem(), popupWindow, centeredPosition);
+ VERIFY_LOCAL_POS(popup, centeredPosition);
+}
+
+void tst_QQuickPopup::popupWindowModality()
+{
+ QSKIP("The behavior isn't correctly implemented yet. Waiting for patch in qtbase");
+
+ QQuickApplicationHelper helper(this, "popupWithButtonInBackground.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ QVERIFY(popup);
+
+ auto *popupPrivate = QQuickPopupPrivate::get(popup);
+ QVERIFY(popupPrivate);
+
+ if (!popupPrivate->usePopupWindow())
+ QSKIP("The platform doesn't support native popup windows. Skipping test.");
+
+ auto *button = window->findChild<QQuickButton *>();
+ QVERIFY(button);
+
+ QSignalSpy buttonSpy(button, SIGNAL(clicked()));
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+
+ auto *popupWindow = popupPrivate->popupWindow;
+ QVERIFY(popupWindow);
+ QVERIFY(popupWindow->isVisible());
+ // NonModal by default
+ QCOMPARE(popupWindow->modality(), Qt::NonModal);
+
+ // Non modal popups should close on press outside
+ QTest::mouseClick(helper.window, Qt::LeftButton, Qt::NoModifier, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
+ QTRY_COMPARE(buttonSpy.count(), 1);
+ QVERIFY(!popupWindow->isVisible());
+
+ popup->setModal(true);
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+ QVERIFY(popupWindow->isVisible());
+ QCOMPARE(popupWindow->modality(), Qt::ApplicationModal);
+
+ // Pressing outside the popup shouldn't cause the button to get the event, because of modality.
+ QTest::mouseClick(helper.window, Qt::LeftButton, Qt::NoModifier, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
+ QCoreApplication::processEvents();
+ QCOMPARE(buttonSpy.count(), 1);
+ QVERIFY(popupWindow->isVisible());
+
+ popup->close();
+ QTRY_VERIFY(!popup->isVisible());
+}
+
+void tst_QQuickPopup::popupWindowClosesOnParentWindowClosing()
+{
+ QSKIP("The behavior isn't correctly implemented yet. Waiting for patch in qtbase");
+ QQuickApplicationHelper helper(this, "simplepopup.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ QVERIFY(popup);
+ auto *popupPrivate = QQuickPopupPrivate::get(popup);
+ QVERIFY(popupPrivate);
+
+ if (!popupPrivate->usePopupWindow())
+ QSKIP("The platform doesn't support native popup windows. Skipping test.");
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+
+ auto *popupWindow = popupPrivate->popupWindow;
+ QVERIFY(popupWindow);
+ QVERIFY(popupWindow->isVisible());
+
+ // Closing parent window, should close child window;
+ window->close();
+
+ QTRY_VERIFY(!window->isVisible());
+ QTRY_VERIFY(!popupWindow->isVisible());
+}
+
+void tst_QQuickPopup::popupWindowChangingParent()
+{
+ QSKIP("Enable test later");
+ QQuickApplicationHelper helper(this, "reparentingPopup.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ QVERIFY(popup);
+ auto *popupPrivate = QQuickPopupPrivate::get(popup);
+ QVERIFY(popupPrivate);
+
+ QQuickItem *item1 = window->property("rectangle1").value<QQuickItem *>();
+ QVERIFY(item1);
+
+ QQuickItem *item2 = window->property("rectangle2").value<QQuickItem *>();
+ QVERIFY(item2);
+
+ QQuickItem *item3 = window->property("rectangle3").value<QQuickItem *>();
+ QVERIFY(item3);
+
+ if (!popupPrivate->usePopupWindow())
+ QSKIP("The platform doesn't support native popup windows. Skipping test.");
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+
+ auto *popupWindow = popupPrivate->popupWindow;
+ QVERIFY(popupWindow);
+ QVERIFY(popupWindow->isVisible());
+
+ const QPoint initialPos(10, 10);
+
+ VERIFY_GLOBAL_POS(item1, popupWindow, initialPos);
+ VERIFY_LOCAL_POS(popup, initialPos);
+
+ popup->setParentItem(item1);
+
+ VERIFY_GLOBAL_POS(item1, popupWindow, initialPos);
+ VERIFY_LOCAL_POS(popup, initialPos);
+
+ popup->setParentItem(item2);
+
+ VERIFY_GLOBAL_POS(item2, popupWindow, initialPos);
+ VERIFY_LOCAL_POS(popup, initialPos);
+
+ popup->setParentItem(item3);
+
+ VERIFY_GLOBAL_POS(item3, popupWindow, initialPos);
+ VERIFY_LOCAL_POS(popup, initialPos);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
index 2afcd81a44..f4e35a37bf 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
@@ -814,7 +814,6 @@ void tst_QQuickFolderDialogImpl::itemsDisabledWhenNecessary()
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(openButton->isEnabled(), false);
#endif
-
// Hide it with the escape key. The Open button should now be enabled.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
QVERIFY(!breadcrumbBar->textField()->isVisible());