From 6243befa23d91b3a1b12a738ac9316c10479086f Mon Sep 17 00:00:00 2001 From: Mengci Cai Date: Thu, 19 Mar 2026 15:22:13 +0800 Subject: [PATCH] feat: improve tray menu and notification panel interaction Added tray menu state tracking and automatic panel management to prevent conflicts between tray menus and notification panel. The changes include: 1. Added autoCloseOnDeactivate property to PanelMenu for conditional auto-close behavior 2. Implemented tray menu state tracking in MenuHelper with hasTrayMenuOpen property 3. Added delayed menu opening with timers to handle panel transitions smoothly 4. Modified notification center panel to respect tray menu state and prevent overlapping 5. Added automatic notification panel closing when opening app item menus or tray menus Log: Improved tray menu behavior and fixed conflicts with notification panel Influence: 1. Test opening tray menus while notification panel is visible - should auto-close notification panel 2. Verify notification panel cannot be opened when tray menu is active 3. Test app item menu opening behavior with notification panel 4. Check tray menu auto-close behavior when losing focus 5. Verify all menu types properly track their open/close state 6. Test timing of menu openings and panel transitions 7. Verify notification panel visibility respects tray menu state PMS: BUG--284867 --- frame/qml/PanelMenu.qml | 4 +- panels/dock/MenuHelper.qml | 4 +- panels/dock/taskmanager/package/AppItem.qml | 4 ++ panels/dock/tray/SurfacePopup.qml | 61 ++++++++++++++++++- panels/dock/tray/TrayItemSurfacePopup.qml | 61 ++++++++++++++++++- .../center/notificationcenterpanel.cpp | 35 ++++++++--- .../center/notificationcenterpanel.h | 11 +++- panels/notification/center/package/main.qml | 2 +- 8 files changed, 164 insertions(+), 18 deletions(-) diff --git a/frame/qml/PanelMenu.qml b/frame/qml/PanelMenu.qml index a2104214c..44f2466ec 100644 --- a/frame/qml/PanelMenu.qml +++ b/frame/qml/PanelMenu.qml @@ -17,6 +17,7 @@ Item { property bool readyBinding: false // WM_NAME, used for kwin. property string windowTitle: "dde-shell/panelmenu" + property bool autoCloseOnDeactivate: true width: menu.childrenRect.width height: menu.childrenRect.height @@ -83,8 +84,7 @@ Item { { if (!menuWindow) return - // TODO why activeChanged is not emit. - if (menuWindow && !menuWindow.active) { + if (autoCloseOnDeactivate && menuWindow && !menuWindow.active) { control.close() } } diff --git a/panels/dock/MenuHelper.qml b/panels/dock/MenuHelper.qml index 46775f090..842eaa70a 100644 --- a/panels/dock/MenuHelper.qml +++ b/panels/dock/MenuHelper.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -8,12 +8,14 @@ import Qt.labs.platform Item { property Menu activeMenu: null + property bool hasTrayMenuOpen: false signal menuClosed() Connections { target: activeMenu function onAboutToHide() { activeMenu = null + hasTrayMenuOpen = false menuClosed() } } diff --git a/panels/dock/taskmanager/package/AppItem.qml b/panels/dock/taskmanager/package/AppItem.qml index 40eed5b4e..a74408922 100644 --- a/panels/dock/taskmanager/package/AppItem.qml +++ b/panels/dock/taskmanager/package/AppItem.qml @@ -430,6 +430,10 @@ Item { function requestAppItemMenu() { contextMenuLoader.trashEmpty = TaskManager.isTrashEmpty() contextMenuLoader.active = true + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel && notifyPanel.visible) { + notifyPanel.close() + } MenuHelper.openMenu(contextMenuLoader.item) } diff --git a/panels/dock/tray/SurfacePopup.qml b/panels/dock/tray/SurfacePopup.qml index 122ae045e..ab16e4546 100644 --- a/panels/dock/tray/SurfacePopup.qml +++ b/panels/dock/tray/SurfacePopup.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -16,6 +16,52 @@ Item { } property int toolTipVOffset: 0 signal popupCreated(var surfaceId) + property var pendingTrayMenu: null + Timer { + id: trayMenuOpenTimer + interval: 150 + repeat: false + onTriggered: { + if (pendingTrayMenu) { + pendingTrayMenu.open() + pendingTrayMenu = null + } + } + } + Timer { + id: restoreAutoCloseTimer + interval: 200 + repeat: false + onTriggered: { + if (pendingTrayMenu) { + pendingTrayMenu.autoCloseOnDeactivate = true + } + } + } + Connections { + target: menu + function onMenuVisibleChanged() { + if (!menu.menuVisible) { + MenuHelper.hasTrayMenuOpen = false + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel) { + notifyPanel.hasTrayMenuOpen = false + } + } + } + } + Connections { + target: menu.menuWindow + function onActiveChanged() { + if (menu.menuWindow && !menu.menuWindow.active && !menu.menuVisible) { + MenuHelper.hasTrayMenuOpen = false + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel) { + notifyPanel.hasTrayMenuOpen = false + } + } + } + } PanelToolTipWindow { id: toolTipWindow @@ -144,7 +190,18 @@ Item { menu.menuY = Qt.binding(function () { return menu.shellSurface.y }) - menu.open() + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel && notifyPanel.visible) { + notifyPanel.close() + } + menu.autoCloseOnDeactivate = false + MenuHelper.hasTrayMenuOpen = true + if (notifyPanel) { + notifyPanel.hasTrayMenuOpen = true + } + root.pendingTrayMenu = menu + trayMenuOpenTimer.restart() + restoreAutoCloseTimer.restart() } } } diff --git a/panels/dock/tray/TrayItemSurfacePopup.qml b/panels/dock/tray/TrayItemSurfacePopup.qml index d2406a169..f49bf61b0 100644 --- a/panels/dock/tray/TrayItemSurfacePopup.qml +++ b/panels/dock/tray/TrayItemSurfacePopup.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -20,6 +20,52 @@ Item { return false } readonly property bool isOpened: popup.popupVisible + property var pendingTrayMenu: null + Timer { + id: trayMenuOpenTimer + interval: 150 + repeat: false + onTriggered: { + if (pendingTrayMenu) { + pendingTrayMenu.open() + pendingTrayMenu = null + } + } + } + Timer { + id: restoreAutoCloseTimer + interval: 200 + repeat: false + onTriggered: { + if (pendingTrayMenu) { + pendingTrayMenu.autoCloseOnDeactivate = true + } + } + } + Connections { + target: popupMenu + function onMenuVisibleChanged() { + if (!popupMenu.menuVisible) { + MenuHelper.hasTrayMenuOpen = false + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel) { + notifyPanel.hasTrayMenuOpen = false + } + } + } + } + Connections { + target: popupMenu.menuWindow + function onActiveChanged() { + if (popupMenu.menuWindow && !popupMenu.menuWindow.active && !popupMenu.menuVisible) { + MenuHelper.hasTrayMenuOpen = false + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel) { + notifyPanel.hasTrayMenuOpen = false + } + } + } + } function closeTooltip() { if (toolTip.toolTipVisible) { toolTip.close() @@ -161,7 +207,18 @@ Item { var point = Qt.point(popupMenu.shellSurface.x, popupMenu.shellSurface.y) return Qt.rect(point.x, point.y, popupMenu.width, popupMenu.height) }) - popupMenu.open() + let notifyPanel = DS.applet("org.deepin.ds.notificationcenter") + if (notifyPanel && notifyPanel.visible) { + notifyPanel.close() + } + popupMenu.autoCloseOnDeactivate = false + MenuHelper.hasTrayMenuOpen = true + if (notifyPanel) { + notifyPanel.hasTrayMenuOpen = true + } + root.pendingTrayMenu = popupMenu + trayMenuOpenTimer.restart() + restoreAutoCloseTimer.restart() } } } diff --git a/panels/notification/center/notificationcenterpanel.cpp b/panels/notification/center/notificationcenterpanel.cpp index 13f9729af..ff6ffa188 100644 --- a/panels/notification/center/notificationcenterpanel.cpp +++ b/panels/notification/center/notificationcenterpanel.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -9,24 +9,26 @@ #include "notificationcenterproxy.h" #include "notifyaccessor.h" -#include -#include #include -#include #include +#include +#include +#include -#include #include +#include #include #include -#include +#include DS_USE_NAMESPACE -namespace notification { +namespace notification +{ Q_DECLARE_LOGGING_CATEGORY(notifyLog) } -namespace notification { +namespace notification +{ static const QString DDENotifyDBusServer = "org.deepin.dde.Notification1"; static const QString DDENotifyDBusInterface = "org.deepin.dde.Notification1"; @@ -99,6 +101,10 @@ bool NotificationCenterPanel::visible() const void NotificationCenterPanel::setVisible(bool newVisible) { + if (m_hasTrayMenuOpen) { + qDebug(notifyLog) << "Blocked: tray menu is open"; + return; + } if (m_visible == newVisible) return; m_visible = newVisible; @@ -128,6 +134,19 @@ void NotificationCenterPanel::setBubblePanelEnabled(bool enabled) QMetaObject::invokeMethod(applet, "setEnabled", Qt::DirectConnection, Q_ARG(bool, enabled)); } +bool NotificationCenterPanel::hasTrayMenuOpen() const +{ + return m_hasTrayMenuOpen; +} + +void NotificationCenterPanel::setHasTrayMenuOpen(bool hasOpen) +{ + if (m_hasTrayMenuOpen == hasOpen) + return; + m_hasTrayMenuOpen = hasOpen; + emit hasTrayMenuOpenChanged(); +} + D_APPLET_CLASS(NotificationCenterPanel) } diff --git a/panels/notification/center/notificationcenterpanel.h b/panels/notification/center/notificationcenterpanel.h index 2cd546f7c..8bc76c9ae 100644 --- a/panels/notification/center/notificationcenterpanel.h +++ b/panels/notification/center/notificationcenterpanel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,13 +6,15 @@ #include "panel.h" -namespace notification { +namespace notification +{ class NotificationCenterProxy; class NotificationCenterPanel : public DS_NAMESPACE::DPanel { Q_OBJECT Q_PROPERTY(bool visible READ visible NOTIFY visibleChanged FINAL) + Q_PROPERTY(bool hasTrayMenuOpen READ hasTrayMenuOpen WRITE setHasTrayMenuOpen NOTIFY hasTrayMenuOpenChanged FINAL) public: explicit NotificationCenterPanel(QObject *parent = nullptr); ~NotificationCenterPanel(); @@ -24,14 +26,19 @@ class NotificationCenterPanel : public DS_NAMESPACE::DPanel void setVisible(bool newVisible); Q_INVOKABLE void close(); + bool hasTrayMenuOpen() const; + void setHasTrayMenuOpen(bool hasOpen); + Q_SIGNALS: void visibleChanged(); + void hasTrayMenuOpenChanged(); private slots: void setBubblePanelEnabled(bool enabled); private: bool m_visible = false; + bool m_hasTrayMenuOpen = false; NotificationCenterProxy *m_proxy = nullptr; }; } diff --git a/panels/notification/center/package/main.qml b/panels/notification/center/package/main.qml index 9843620df..b79e8eb65 100644 --- a/panels/notification/center/package/main.qml +++ b/panels/notification/center/package/main.qml @@ -80,7 +80,7 @@ Window { } // visible: true - visible: Panel.visible + visible: Panel.visible && !Panel.hasTrayMenuOpen flags: Qt.Tool property int contentPadding: 20