diff --git a/examples/qml-inspect/Example_Popup.qml b/examples/qml-inspect/Example_Popup.qml index 9328b3f5f..e32c6996e 100644 --- a/examples/qml-inspect/Example_Popup.qml +++ b/examples/qml-inspect/Example_Popup.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -68,12 +68,6 @@ Column { } } } - Button { - text: "handle forceWindowMode" - onClicked: { - popupWindow.PopupHandle.forceWindowMode = !popupWindow.PopupHandle.forceWindowMode - } - } Popup { id: popupWindow; objectName: "pupup window" @@ -85,10 +79,25 @@ Column { // width: 300 // height: 300 // margins: 100 - PopupHandle.forceWindowMode: true - PopupHandle.delegate: PopupWindow { - blurControl: popupWindow - } + + // Test window properties in Qt6 + // popupType: Popup.Window + // PopupHandle.windowRadius: 18 + // PopupHandle.borderWidth: 2 + // PopupHandle.borderColor: "red" + // PopupHandle.shadowRadius: 30 + // PopupHandle.shadowOffset: Qt.point(0, 4) + // PopupHandle.shadowColor: Qt.rgba(0, 0, 0, 0.5) + // PopupHandle.translucentBackground: true + // PopupHandle.enableBlurWindow: true + + // Component.onCompleted: { + // console.log("=== Popup Properties ===") + // console.log("windowRadius:", PopupHandle.windowRadius) + // console.log("borderWidth:", PopupHandle.borderWidth) + // console.log("borderColor:", PopupHandle.borderColor) + // console.log("translucentBackground:", PopupHandle.translucentBackground) + // } contentItem: Column { spacing: 10 Text { @@ -125,8 +134,6 @@ Column { Menu { id: menuPopup MenuItem { text: "Text" } - - PopupHandle.forceWindowMode: true } ArrowShapePopup { id: arrow diff --git a/qmlplugin/qmlplugin_plugin.cpp b/qmlplugin/qmlplugin_plugin.cpp index f9a44e6b7..057261d70 100644 --- a/qmlplugin/qmlplugin_plugin.cpp +++ b/qmlplugin/qmlplugin_plugin.cpp @@ -22,7 +22,6 @@ #include "private/dquickiconlabel_p.h" #include "private/dsettingscontainer_p.h" #include "private/dmessagemanager_p.h" -#include "private/dpopupwindowhandle_p.h" #include "private/dobjectmodelproxy_p.h" #include "private/dquickwaterprogressattribute_p.h" #include "private/dquickarrowboxpath_p.h" @@ -208,7 +207,6 @@ void QmlpluginPlugin::registerTypes(const char *uri) QStringLiteral("ColorSelector is only available as an attached property.")); dtkRegisterUncreatableType(uri, implUri, 1, 0, "Color", QStringLiteral("Color is only available as enums.")); - dtkRegisterUncreatableType(uri, implUri, 1, 0, "PopupHandle", "PopupWindow Attached"); dtkRegisterUncreatableType(uri, implUri, 1, 0, "PlatformHandle", "PlatformHandle"); qRegisterMetaType(); diff --git a/qt6/src/qml/Menu.qml b/qt6/src/qml/Menu.qml index 92155b3f0..b3a07729b 100644 --- a/qt6/src/qml/Menu.qml +++ b/qt6/src/qml/Menu.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -39,9 +39,6 @@ T.Menu { delegate: MenuItem { } - D.PopupHandle.delegate: PopupWindow { - blurControl: control - } contentItem: FocusScope { // QTBUG-99897 focus doesn't be clear. @@ -100,7 +97,7 @@ T.Menu { } background: Loader { - active: !control.D.PopupHandle.window + active: control.popupType !== Popup.Window sourceComponent: FloatingPanel { implicitWidth: DS.Style.menu.item.width implicitHeight: DS.Style.menu.item.height diff --git a/qt6/src/qml/Popup.qml b/qt6/src/qml/Popup.qml index 9cb01484c..5394912ce 100644 --- a/qt6/src/qml/Popup.qml +++ b/qt6/src/qml/Popup.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -19,7 +19,7 @@ T.Popup { padding: DS.Style.popup.padding background: Loader { - active: !control.D.PopupHandle.window + active: control.popupType !== Popup.Window sourceComponent: FloatingPanel { implicitHeight: DS.Style.popup.height implicitWidth: DS.Style.popup.width diff --git a/src/dquickwindow.cpp b/src/dquickwindow.cpp index b1c948529..64210d35b 100644 --- a/src/dquickwindow.cpp +++ b/src/dquickwindow.cpp @@ -56,6 +56,11 @@ DQuickWindowAttached *DQuickWindow::qmlAttachedProperties(QObject *object) if (window) { return new DQuickWindowAttached(window); } + + // Support QQuickPopup with popupType == Window + if (object && object->inherits("QQuickPopup")) { + return new DQuickWindowAttached(object); + } return nullptr; } @@ -131,6 +136,10 @@ bool DQuickWindowAttachedPrivate::ensurePlatformHandle() if (handle) return true; + if (!window) { + return false; + } + if (!DPlatformHandle::setEnabledNoTitlebarForWindow(window, true)) { qWarning() << "Failed to enable NoTitlebar for the window:" << window; return false; @@ -179,10 +188,32 @@ void DQuickWindowAttachedPrivate::destoryPlatformHandle() handle = nullptr; } +void DQuickWindowAttachedPrivate::setWindow(QWindow *newWindow) +{ + Q_Q(DQuickWindowAttached); + + if (window == newWindow) + return; + + window = newWindow; + + if (newWindow) { + newWindow->installEventFilter(q); + QObject::connect(DWindowManagerHelper::instance(), SIGNAL(windowMotifWMHintsChanged(quint32)), + q, SLOT(_q_onWindowMotifHintsChanged(quint32)), Qt::UniqueConnection); + + if (explicitEnable == True) { + ensurePlatformHandle(); + } + } +} + void DQuickWindowAttachedPrivate::_q_onWindowMotifHintsChanged(quint32 winId) { D_Q(DQuickWindowAttached); + if (!q->window()) + return; if (q->window()->winId() != winId) return; @@ -339,9 +370,22 @@ DQuickWindowAttached::DQuickWindowAttached(QWindow *window) this, SLOT(_q_onWindowMotifHintsChanged(quint32))); } +DQuickWindowAttached::DQuickWindowAttached(QObject *popupObject) + : QObject(popupObject) + , DObject(*new DQuickWindowAttachedPrivate(nullptr, this)) +{ +} + QQuickWindow *DQuickWindowAttached::window() const { - return qobject_cast(parent()); + D_DC(DQuickWindowAttached); + return qobject_cast(d->window); +} + +void DQuickWindowAttached::setWindow(QQuickWindow *window) +{ + D_D(DQuickWindowAttached); + d->setWindow(window); } /*! diff --git a/src/dquickwindow.h b/src/dquickwindow.h index a312a72c5..db67525b4 100644 --- a/src/dquickwindow.h +++ b/src/dquickwindow.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -80,8 +80,10 @@ class DQuickWindowAttached : public QObject, public DTK_CORE_NAMESPACE::DObject public: explicit DQuickWindowAttached(QWindow *window); + explicit DQuickWindowAttached(QObject *popupObject); QQuickWindow *window() const; + void setWindow(QQuickWindow *window); bool isEnabled() const; int windowRadius() const; diff --git a/src/private/dpopupwindowhandle.cpp b/src/private/dpopupwindowhandle.cpp index 8472663af..647269a44 100644 --- a/src/private/dpopupwindowhandle.cpp +++ b/src/private/dpopupwindowhandle.cpp @@ -1,265 +1,285 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later -#define protected public #include "dpopupwindowhandle_p.h" +#include "dquickwindow.h" #include #include +#include +#include +#include +#include +#include +#include -#include +#include -DCORE_USE_NAMESPACE DQUICK_BEGIN_NAMESPACE -// className prepend string of QT_NAMESPACE if existed. -bool inheritsTheClassType(QObject *object, QString className) { -#if defined(QT_NAMESPACE) -#define D_GET_NAMESPACE_STR_IMPL(M) #M "::" -#define D_GET_NAMESPACE_STR(M) D_GET_NAMESPACE_STR_IMPL(M) - className.prepend(D_GET_NAMESPACE_STR(QT_NAMESPACE)); -#undef D_GET_NAMESPACE_STR -#undef D_GET_NAMESPACE_STR_IMPL -#endif - return object && object->inherits(qPrintable(className)); -} - -static inline bool shouldCreatePopupWindowForMode(const DQMLGlobalObject::PopupMode mode) +static bool isPopupWindow(QWindow *window) { - switch (mode) { - case DQMLGlobalObject::WindowMode: - return true; - case DQMLGlobalObject::EmbedMode: - return false; - case DQMLGlobalObject::AutoMode: - // TODO https://github.com/linuxdeepin/dtk/issues/70 - if (qEnvironmentVariableIsEmpty("D_POPUP_MODE")) - return false; - return qEnvironmentVariable("D_POPUP_MODE") != "embed"; - } - return false; + return window && window->inherits("QQuickPopupWindow"); } -DQMLGlobalObject::PopupMode DPopupWindowHandle::m_popupMode = DQMLGlobalObject::AutoMode; -DPopupWindowHandle::DPopupWindowHandle(QObject *parent) - : QObject (parent) + +static bool isPopupItem(QQuickItem *item) { - // after `autoWindowMode` property initialized to createHandle. - connect(popup(), SIGNAL(windowChanged(QQuickWindow *)), this, SLOT(createHandle())); + return item && item->inherits("QQuickPopupItem"); } DPopupWindowHandle::~DPopupWindowHandle() { + if (m_trackedItem) + QQuickItemPrivate::get(m_trackedItem)->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + if (m_parentWindow) + m_parentWindow->removeEventFilter(this); + if (m_popupWin) + m_popupWin->removeEventFilter(this); } -DPopupWindowHandle *DPopupWindowHandle::qmlAttachedProperties(QObject *object) +DPopupWindowHandle::DPopupWindowHandle(QObject *popup) + : QObject(popup) + , m_popup(popup) { - if (!inheritsTheClassType(object, "QQuickPopup")) - return nullptr; - - return new DPopupWindowHandle(object); -} + m_attached = DQuickWindow::qmlAttachedProperties(popup); + if (!m_attached) + return; + + // Initial update + updateEnabled(); + + popupItemReparented(); -void DPopupWindowHandle::setPopupMode(const DQMLGlobalObject::PopupMode mode) -{ - m_popupMode = mode; + connect(popup, SIGNAL(popupTypeChanged()), this, SLOT(updateEnabled())); } -QQuickWindow *DPopupWindowHandle::window() const +DQuickWindowAttached *DPopupWindowHandle::qmlAttachedProperties(QObject *object) { - return m_handle ? m_handle->window() : nullptr; + auto handle = new DPopupWindowHandle(object); + return handle->m_attached; } -QQmlComponent *DPopupWindowHandle::delegate() const +DQuickWindowAttached *DPopupWindowHandle::windowAttached() const { - return m_delegate; + return m_attached; } -void DPopupWindowHandle::setDelegate(QQmlComponent *delegate) +QQuickItem *DPopupWindowHandle::popupItem() const { - m_delegate = delegate; -} + if (!m_popup) + return nullptr; -bool DPopupWindowHandle::forceWindowMode() const -{ - return m_forceWindowMode; + const auto children = m_popup->findChildren(Qt::FindDirectChildrenOnly); + auto it = std::find_if(children.begin(), children.end(), isPopupItem); + if (it != children.end()) + return *it; + return nullptr; } -void DPopupWindowHandle::setForceWindowMode(bool forceWindowMode) +void DPopupWindowHandle::popupItemReparented() { - if (m_forceWindowMode == forceWindowMode) + QQuickItem *item = popupItem(); + if (m_trackedItem == item) return; - m_forceWindowMode = forceWindowMode; - if (!m_forceWindowMode && m_handle) { - m_handle.reset(); - Q_EMIT windowChanged(); + if (m_trackedItem) { + QQuickItemPrivate::get(m_trackedItem)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | QQuickItemPrivate::Parent); + disconnect(m_trackedItem, &QQuickItem::windowChanged, this, &DPopupWindowHandle::onWindowChanged); } - if (m_forceWindowMode) { - // try to create handle. - createHandle(); - } -} -void DPopupWindowHandle::createHandle() -{ - if (!needCreateHandle()) - return; + m_trackedItem = item; - auto window = qobject_cast(m_delegate->create(m_delegate->creationContext())); - Q_ASSERT(window); + // QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | QQuickItemPrivate::Parent); + connect(item, &QQuickItem::windowChanged, this, &DPopupWindowHandle::onWindowChanged); - m_handle.reset(new DPopupWindowHandleImpl(window, popup())); - Q_EMIT windowChanged(); + + if (QQuickWindow *window = popupWindow()) + m_attached->setWindow(window); } -bool DPopupWindowHandle::needCreateHandle() const +QQuickWindow *DPopupWindowHandle::popupWindow() const { - // has created. - if (m_handle) - return false; - - // no delegate. - if (!m_delegate) { - if (m_forceWindowMode) - qWarning() << "delegate don't set but forceWindowMode has been set."; - - return false; - } - // forceWindowMode > `D_POPUP_MODE` > popupMode - return m_forceWindowMode || shouldCreatePopupWindowForMode(m_popupMode); + QQuickItem *item = popupItem(); + return item ? item->window() : nullptr; } -QObject *DPopupWindowHandle::popup() const +void DPopupWindowHandle::updateEnabled() { - return parent(); + if (!m_popup || !m_attached) + return; + + QVariant popupTypeVar = m_popup->property("popupType"); + bool shouldEnable = popupTypeVar.isValid() && popupTypeVar.toInt() == 1; + if (shouldEnable == m_enabled) + return; + m_enabled = shouldEnable; + m_attached->setEnabled(shouldEnable); } -// it's not to call QQuickPopupItem's reposition when handle is positioning. -static constexpr char const *PopupWindowHandlePointer = "_d_popup_window_handle"; -static inline void popupGeometryChanged(QQuickItem *obj, const QRectF &newGeometry, const QRectF &oldGeometry) +bool DPopupWindowHandle::isEnabled() const { - DPopupWindowHandleImpl *handle = obj->property(PopupWindowHandlePointer).value(); - Q_ASSERT(handle); - if (!handle->isPositioning()) { - // only in `reposition` to override virtual function. -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DVtableHook::callOriginalFun(obj, &QQuickItem::geometryChanged, newGeometry, oldGeometry); -#else - DVtableHook::callOriginalFun(obj, &QQuickItem::geometryChange, newGeometry, oldGeometry); -#endif - } + return m_enabled; } -static inline void popupUpdatePolish(QQuickItem *obj) + +void DPopupWindowHandle::onWindowChanged(QQuickWindow *window) { - DPopupWindowHandleImpl *handle = obj->property(PopupWindowHandlePointer).value(); - Q_ASSERT(handle); - if (handle->isPositioning()) { - // avoid to call original function in `reposition`. - handle->setPositioning(false); - } else { - // only sepcial scene to override virtual function. - DVtableHook::callOriginalFun(obj, &QQuickItem::updatePolish); + // Cleanup old filters + if (m_popupWin) { + m_popupWin->removeEventFilter(this); + m_popupWin = nullptr; + } + if (m_parentWindow) { + m_parentWindow->removeEventFilter(this); + m_parentWindow = nullptr; } -} -DPopupWindowHandleImpl::DPopupWindowHandleImpl(QQuickWindow *window, QObject *parent) - : QObject(parent) - , m_window(window) - , m_popup(parent) -{ - Q_ASSERT(popupItem()); - - connect(popup(), SIGNAL(opened()), this, SLOT(reposition())); - popupItem()->setProperty(PopupWindowHandlePointer, QVariant::fromValue(this)); - // geometryChanged would call reposition of `PopupItem`. -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DVtableHook::overrideVfptrFun(popupItem(), &QQuickItem::geometryChanged, &popupGeometryChanged); -#else - DVtableHook::overrideVfptrFun(popupItem(), &QQuickItem::geometryChange, &popupGeometryChanged); -#endif - // updatePolish would call reposition of `PopupItem`. - DVtableHook::overrideVfptrFun(popupItem(), &QQuickItem::updatePolish, &popupUpdatePolish); - - // TODO QML Window with Qt::Popup flag not behaving correctly (QTBUG-69777) - connect(m_window, &QWindow::activeChanged, this, &DPopupWindowHandleImpl::close); - connect(popup(), SIGNAL(closed()), this, SLOT(close())); + // Apply attached properties (blur, radius, etc.) to popup windows. + if (m_attached) { + if (!window || isPopupWindow(window)) + m_attached->setWindow(window); + } + + m_popupWin = window; + if (window && isEnabled() && isPopupWindow(window)) { + + window->installEventFilter(this); + + // Install event filter on parent window for close-on-click. + // Done here so it is registered once per popup window instance. + if (QQuickWindow *main = qobject_cast(window->transientParent())) { + m_parentWindow = main; + m_parentWindow->installEventFilter(this); + } + } } -DPopupWindowHandleImpl::~DPopupWindowHandleImpl() +bool DPopupWindowHandle::eventFilter(QObject *watched, QEvent *event) { - QQuickItem *item = popupItem(); - if (item) { - // reset original virtual function. -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DVtableHook::resetVfptrFun(item, &QQuickItem::geometryChanged); -#else - DVtableHook::resetVfptrFun(item, &QQuickItem::geometryChange); -#endif - DVtableHook::resetVfptrFun(item, &QQuickItem::updatePolish); - disconnect(item, nullptr, this, nullptr); + if (watched == m_popupWin && event->type() == QEvent::Move) { + adjustPopupPosition(); } - disconnect(popup(), nullptr, this, nullptr); - disconnect(m_window, nullptr, this, nullptr); - m_window->deleteLater(); - m_window = nullptr; -} + // Close popup on parent window click (only while popup is visible) + if (watched == m_parentWindow && event->type() == QEvent::MouseButtonPress + && m_popup->property("visible").toBool()) { + int closePolicy = m_popup->property("closePolicy").toInt(); + bool closeOnPressOutside = closePolicy & 0x01; + bool closeOnPressOutsideParent = closePolicy & 0x02; -QQuickWindow *DPopupWindowHandleImpl::window() const -{ - return m_window; + if (closeOnPressOutside || closeOnPressOutsideParent) { + QMetaObject::invokeMethod(m_popup, "close", Qt::QueuedConnection); + } + } + + return QObject::eventFilter(watched, event); } -QObject *DPopupWindowHandleImpl::popup() const +void DPopupWindowHandle::itemGeometryChanged(QQuickItem *, + QQuickGeometryChange change, + const QRectF &) { - return m_popup; -} + if (!change.positionChange() && !change.sizeChange()) + return; -QQuickItem *DPopupWindowHandleImpl::popupItem() const { - for (auto item : popup()->children()) { - if (inheritsTheClassType(item, "QQuickPopupItem")) - return qobject_cast(item); - } - return nullptr; + adjustPopupPosition(); } -bool DPopupWindowHandleImpl::isPositioning() const +void DPopupWindowHandle::itemVisibilityChanged(QQuickItem *item) { - return m_positioning; + if (!item->isVisible()) + return; + + adjustPopupPosition(); } -void DPopupWindowHandleImpl::setPositioning(bool positioning) +void DPopupWindowHandle::itemParentChanged(QQuickItem *item, QQuickItem *) { - m_positioning = positioning; + if (!item) + return; + + adjustPopupPosition(); } -void DPopupWindowHandleImpl::reposition() +void DPopupWindowHandle::adjustPopupPosition() { - if (isPositioning()) + if (!isEnabled() ||!m_popupWin) return; - setPositioning(true); - - m_window->resize(popupItem()->size().toSize()); - // set window's position to origin popupItem' leftTop position. - m_window->setPosition(popupItem()->mapToGlobal(QPoint(0, 0)).toPoint()); - // reset popupItem's position to window's contentItem leftTop position. - popupItem()->setPosition(m_window->contentItem()->position()); - popupItem()->setParentItem(m_window->contentItem()); + const QSize size = m_popupWin->size(); + if (size.width() <= 0 || size.height() <= 0) + return; - m_window->show(); - m_window->requestActivate(); -} + // Nested popup (submenu) detection: + // A submenu's popupWindow uses the parent menu's popupWindow as its transientParent. + QWindow *transient = m_popupWin->transientParent(); + const bool isNested = isPopupWindow(transient); + QRectF parentWindowRect; + if (isNested) + parentWindowRect = QRectF(QPointF(transient->position()), QSizeF(transient->size())); + + // Screen selection: + // - Nested popups (submenus) must use the parent menu's screen to avoid + // screenAt() returning an adjacent screen or null when the submenu's + // initial position is already off-screen. + // - Flat popups use their own position to determine the screen. + QScreen *screen = nullptr; + if (isNested) { + screen = transient->screen(); + } else { + const QPoint winCenter = m_popupWin->position() + QPoint(size.width() / 2, size.height() / 2); + screen = QGuiApplication::screenAt(winCenter); + } + if (!screen) + screen = m_popupWin->screen(); + if (!screen) + return; -void DPopupWindowHandleImpl::close() -{ - if (!m_window->isActive() || !popup()->property("visible").toBool()) { - m_window->hide(); - // window hide but popup's visible is true, and it effects popup next open. - popup()->setProperty("visible", false); + const QRectF bounds(screen->availableGeometry()); + QRectF rect(QPointF(m_popupWin->position()), QSizeF(size)); + + // Horizontal flip for submenus: + // Qt places submenus to the right of the parent menu by default; + // flip to the left if there is not enough space on the right, and vice versa. + if (isNested && !parentWindowRect.isNull()) { + const bool overflowsRight = rect.right() > bounds.right(); + const bool overflowsLeft = rect.left() < bounds.left(); + + if (overflowsRight && !overflowsLeft) { + // Not enough space on the right — try flipping to the left of the parent. + const qreal leftCandidate = parentWindowRect.left() - size.width(); + if (leftCandidate >= bounds.left()) { + rect.moveLeft(leftCandidate); + } else { + // Not enough space on either side — push against the right edge. + rect.moveRight(bounds.right()); + } + } else if (overflowsLeft && !overflowsRight) { + // Not enough space on the left — flip to the right of the parent. + const qreal rightCandidate = parentWindowRect.right(); + if (rightCandidate + size.width() <= bounds.right()) { + rect.moveLeft(rightCandidate); + } else { + rect.moveLeft(bounds.left()); + } + } } + + // Vertical clamping and final bounds enforcement. + if (rect.right() > bounds.right()) + rect.moveRight(bounds.right()); + if (rect.left() < bounds.left()) + rect.moveLeft(bounds.left()); + if (rect.bottom() > bounds.bottom()) + rect.moveBottom(bounds.bottom()); + if (rect.top() < bounds.top()) + rect.moveTop(bounds.top()); + + const QPoint newPos = rect.topLeft().toPoint(); + if (newPos != m_popupWin->position()) + m_popupWin->setPosition(newPos); } + DQUICK_END_NAMESPACE #include "moc_dpopupwindowhandle_p.cpp" diff --git a/src/private/dpopupwindowhandle_p.h b/src/private/dpopupwindowhandle_p.h index d99e85bfc..430189202 100644 --- a/src/private/dpopupwindowhandle_p.h +++ b/src/private/dpopupwindowhandle_p.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -10,79 +10,64 @@ #include "dqmlglobalobject_p.h" +#include + QT_BEGIN_NAMESPACE class QQuickWindow; class QQuickItem; QT_END_NAMESPACE - DQUICK_BEGIN_NAMESPACE -class DPopupWindowHandleImpl; -class Q_DECL_EXPORT DPopupWindowHandle : public QObject +class DQuickWindowAttached; + +class DPopupWindowHandle : public QObject, public QQuickItemChangeListener { Q_OBJECT - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged) - Q_PROPERTY(bool forceWindowMode READ forceWindowMode WRITE setForceWindowMode) -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QML_UNCREATABLE("PopupWindow Attached.") + + QML_UNCREATABLE("PopupHandle Attached.") QML_NAMED_ELEMENT(PopupHandle) - QML_ATTACHED(DPopupWindowHandle) -#endif - + QML_ATTACHED(DQuickWindowAttached) + public: - explicit DPopupWindowHandle(QObject *parent = nullptr); + explicit DPopupWindowHandle(QObject *popup); ~DPopupWindowHandle() override; - static DPopupWindowHandle *qmlAttachedProperties(QObject *object); + static DQuickWindowAttached *qmlAttachedProperties(QObject *object); - static void setPopupMode(const DQMLGlobalObject::PopupMode mode); + DQuickWindowAttached *windowAttached() const; - QQuickWindow *window() const; - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *delegate); - bool forceWindowMode() const; - void setForceWindowMode(bool forceWindowMode); +protected: + bool eventFilter(QObject *watched, QEvent *event) override; -Q_SIGNALS: - void windowChanged(); + // QQuickItemChangeListener + void itemGeometryChanged(QQuickItem *item, + QQuickGeometryChange change, + const QRectF &oldGeometry) override; -private Q_SLOTS: - void createHandle(); - -private: - QObject *popup() const; - bool needCreateHandle() const; + void itemVisibilityChanged(QQuickItem *item) override; -private: - bool m_forceWindowMode = false; - bool m_isWindowMode = false; - QQmlComponent *m_delegate = nullptr; - QScopedPointer m_handle; - static DQMLGlobalObject::PopupMode m_popupMode; -}; + void itemParentChanged(QQuickItem *item, QQuickItem *oldParent) override; -class DPopupWindowHandleImpl : public QObject -{ - Q_OBJECT -public: - explicit DPopupWindowHandleImpl(QQuickWindow *window, QObject *parent); - ~DPopupWindowHandleImpl() override; +private Q_SLOTS: + void updateEnabled(); + void onWindowChanged(QQuickWindow *window); - QQuickWindow *window() const; - QObject *popup() const; +private: + QQuickWindow *popupWindow() const; QQuickItem *popupItem() const; - void updatePosition(); - bool isPositioning() const; - void setPositioning(bool positioning); + void popupItemReparented(); -private Q_SLOTS: - void reposition(); - void close(); + bool isEnabled() const; + void adjustPopupPosition(); + private: - QQuickWindow *m_window = nullptr; QObject *m_popup = nullptr; - bool m_positioning = false; + + bool m_enabled = false; + DQuickWindowAttached *m_attached = nullptr; + QPointer m_parentWindow = nullptr; + QPointer m_popupWin = nullptr; + QPointer m_trackedItem = nullptr; }; DQUICK_END_NAMESPACE diff --git a/src/private/dqmlglobalobject.cpp b/src/private/dqmlglobalobject.cpp index 05695cf10..8ea840fac 100644 --- a/src/private/dqmlglobalobject.cpp +++ b/src/private/dqmlglobalobject.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -517,11 +517,6 @@ void DQMLGlobalObject::sendSystemMessage(const QString &summary, const QString & }); } -void DQMLGlobalObject::setPopupMode(const PopupMode mode) -{ - DPopupWindowHandle::setPopupMode(mode); -} - bool DQMLGlobalObject::loadTranslator() { DCORE_USE_NAMESPACE; diff --git a/src/private/dqmlglobalobject_p.h b/src/private/dqmlglobalobject_p.h index f73724065..b40f92e88 100644 --- a/src/private/dqmlglobalobject_p.h +++ b/src/private/dqmlglobalobject_p.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -170,13 +170,6 @@ class DQMLGlobalObject : public QObject, public DTK_CORE_NAMESPACE::DObject }; Q_ENUM(ZOrder) - enum PopupMode { - AutoMode, - WindowMode, - EmbedMode - }; - Q_ENUM(PopupMode) - #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) enum class CompositionMode { Source = QPainter::CompositionMode_Source, @@ -243,7 +236,6 @@ class DQMLGlobalObject : public QObject, public DTK_CORE_NAMESPACE::DObject const QString &appIcon = QString(), const QStringList &actions = QStringList(), const QVariantMap hints = QVariantMap(), const int timeout = 3000, const uint replaceId = 0); - static void setPopupMode(const PopupMode mode); static bool loadTranslator(); #if QT_VERSION_MAJOR > 5 diff --git a/src/private/dquickwindow_p.h b/src/private/dquickwindow_p.h index a9f8633a1..801279e78 100644 --- a/src/private/dquickwindow_p.h +++ b/src/private/dquickwindow_p.h @@ -41,6 +41,7 @@ class DQuickWindowAttachedPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate bool ensurePlatformHandle(); void destoryPlatformHandle(); + void setWindow(QWindow *newWindow); void _q_onWindowMotifHintsChanged(quint32 winId); void addBlur(DQuickBehindWindowBlur *blur); void removeBlur(DQuickBehindWindowBlur *blur); diff --git a/src/qml/Menu.qml b/src/qml/Menu.qml index 559f6b1d3..226c2e1bd 100644 --- a/src/qml/Menu.qml +++ b/src/qml/Menu.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -36,10 +36,6 @@ T.Menu { delegate: MenuItem { } - D.PopupHandle.delegate: PopupWindow { - blurControl: control - } - contentItem: Control { contentItem: ColumnLayout { id: viewLayout @@ -82,7 +78,6 @@ T.Menu { } background: Loader { - active: !control.D.PopupHandle.window sourceComponent: FloatingPanel { implicitWidth: DS.Style.menu.item.width implicitHeight: DS.Style.menu.item.height diff --git a/src/qml/Popup.qml b/src/qml/Popup.qml index 5da5b8d13..7c80dfb77 100644 --- a/src/qml/Popup.qml +++ b/src/qml/Popup.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -19,7 +19,6 @@ T.Popup { padding: DS.Style.popup.padding background: Loader { - active: !control.D.PopupHandle.window sourceComponent: FloatingPanel { implicitHeight: DS.Style.popup.height implicitWidth: DS.Style.popup.width diff --git a/src/src.cmake b/src/src.cmake index 9d7b05ea0..e693c8576 100644 --- a/src/src.cmake +++ b/src/src.cmake @@ -35,11 +35,13 @@ if (DTK5) ${PROJECT_SOURCE_DIR}/src/private/dbackdropnode_p.h ${PROJECT_SOURCE_DIR}/src/private/dquickbackdropblitter_p.h ${PROJECT_SOURCE_DIR}/src/private/dquickborderimage_p.h + ${PROJECT_SOURCE_DIR}/src/private/dpopupwindowhandle_p.h ) list(REMOVE_ITEM SRCS ${PROJECT_SOURCE_DIR}/src/private/dbackdropnode.cpp ${PROJECT_SOURCE_DIR}/src/private/dquickbackdropblitter.cpp ${PROJECT_SOURCE_DIR}/src/private/dquickborderimage.cpp + ${PROJECT_SOURCE_DIR}/src/private/dpopupwindowhandle.cpp ) endif()