diff options
| author | Oliver Eftevaag <oliver.eftevaag@qt.io> | 2024-01-25 14:45:04 +0100 |
|---|---|---|
| committer | Oliver Eftevaag <oliver.eftevaag@qt.io> | 2024-05-27 16:12:39 +0200 |
| commit | 2874586cc848397ab649b49b2d39daf4c86d95fc (patch) | |
| tree | bd640c17a5b2f050b863437425851a200cb503a3 /src/quicktemplates/qquickpopupwindow.cpp | |
| parent | bd8d86c341f3791978804446f79adb36cd656b19 (diff) | |
Give QQuickPopup the ability to create top-level windows
Desktop users typically expect dialogs to be top level windows,
but there can also be advantages to having other types of popups,
such as Menus, and ToolTips, be top level windows as well.
Currently the QQuickPopup class is tightly coupled with the
QQuickPopupItem which it creates, and forwards a bunch of properties
to. But we want Popup to seamlessly create a new window, which renders
the popup item. This should serve as a new feature, which is primarily
targeted for desktop users, and should not replace the old behavior,
since having the popup item in the same window will likely be preferred
for mobile and embedded users.
The default behavior will now be to create a popup window, when the QML
application is running on a desktop system (determined by the
platformIntegrations capabilities). It's also possible to opt out of
this new behavior, by setting the AA_DontUsePopupWindows application
attribute flag.
The dialogs in QtQuick.Dialogs should now also appear in their own
dedicated window, since they were using QQuickDialog behind the scenes.
The combobox popup, will also appear in its own window.
Some changes, such as modality, won't take effect until the window is
reopened, since the window is destroyed/recreated on visibility changes.
Done-with: Ed Cooke <ed.cooke@qt.io>
Task-number: QTBUG-121363
Change-Id: I21f4fd1f922219359d3258854bdcdc9be6980f4f
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Diffstat (limited to 'src/quicktemplates/qquickpopupwindow.cpp')
| -rw-r--r-- | src/quicktemplates/qquickpopupwindow.cpp | 171 |
1 files changed, 171 insertions, 0 deletions
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 + |
