diff options
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()); |
