From 8b79d1dad30791fcf66017152cb36e884bef3a87 Mon Sep 17 00:00:00 2001 From: bbedward Date: Thu, 2 Jul 2026 21:32:10 -0400 Subject: [PATCH] notifications: fix blur on popout fixes #2288 --- .../ControlCenter/Details/BluetoothDetail.qml | 2 +- .../ControlCenter/Details/NetworkDetail.qml | 4 +- .../Notifications/Center/NotificationCard.qml | 2 +- .../Notifications/NotificationContextMenu.qml | 159 ++++++++++++++++++ .../Notifications/Popup/NotificationPopup.qml | 106 ++---------- 5 files changed, 181 insertions(+), 92 deletions(-) create mode 100644 quickshell/Modules/Notifications/NotificationContextMenu.qml diff --git a/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml b/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml index 7f8b2b52b..fe423c3be 100644 --- a/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml @@ -607,7 +607,7 @@ Rectangle { readonly property bool showCodecOption: hasDevice && deviceConnected && BluetoothService.isAudioDevice(currentDevice) background: Rectangle { - color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) radius: Theme.cornerRadius border.width: 0 border.color: Theme.outlineStrong diff --git a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml index 80839326e..73d0339d1 100644 --- a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml @@ -395,7 +395,7 @@ Rectangle { property bool currentConnected: false background: Rectangle { - color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) radius: Theme.cornerRadius border.width: 0 border.color: Theme.outlineStrong @@ -793,7 +793,7 @@ Rectangle { } background: Rectangle { - color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) radius: Theme.cornerRadius border.width: 0 border.color: Theme.outlineStrong diff --git a/quickshell/Modules/Notifications/Center/NotificationCard.qml b/quickshell/Modules/Notifications/Center/NotificationCard.qml index 50584cf91..1f3315bdb 100644 --- a/quickshell/Modules/Notifications/Center/NotificationCard.qml +++ b/quickshell/Modules/Notifications/Center/NotificationCard.qml @@ -1060,7 +1060,7 @@ Rectangle { closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside background: Rectangle { - color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) radius: Theme.cornerRadius border.width: 0 border.color: Theme.outlineStrong diff --git a/quickshell/Modules/Notifications/NotificationContextMenu.qml b/quickshell/Modules/Notifications/NotificationContextMenu.qml new file mode 100644 index 000000000..ab64f206d --- /dev/null +++ b/quickshell/Modules/Notifications/NotificationContextMenu.qml @@ -0,0 +1,159 @@ +import QtQuick +import Quickshell +import Quickshell.Wayland +import qs.Common +import qs.Services +import qs.Widgets + +PanelWindow { + id: root + + property string appName: "" + property string desktopEntry: "" + property point anchorPos: Qt.point(0, 0) + + signal muted + signal dismissRequested + + readonly property bool isMuted: SettingsData.isAppMuted(appName, desktopEntry) + + function showAt(x, y, targetScreen) { + if (targetScreen) + screen = targetScreen; + anchorPos = Qt.point(x, y); + visible = true; + } + + function closeMenu() { + visible = false; + } + + function triggerAction(action) { + switch (action) { + case "rules": + SettingsData.addNotificationRuleForNotification(appName, desktopEntry); + PopoutService.openSettingsWithTab("notifications"); + break; + case "mute": + if (isMuted) { + SettingsData.removeMuteRuleForApp(appName, desktopEntry); + } else { + SettingsData.addMuteRuleForApp(appName, desktopEntry); + muted(); + } + break; + case "dismiss": + dismissRequested(); + break; + } + closeMenu(); + } + + WindowBlur { + targetWindow: root + blurX: menuRect.x + blurY: menuRect.y + blurWidth: root.visible ? menuRect.width : 0 + blurHeight: root.visible ? menuRect.height : 0 + blurRadius: Theme.cornerRadius + } + + WlrLayershell.namespace: "dms:notification-context-menu" + WlrLayershell.layer: WlrLayershell.Overlay + WlrLayershell.exclusiveZone: -1 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.None + visible: false + color: "transparent" + + anchors { + top: true + left: true + right: true + bottom: true + } + + Connections { + target: PopoutManager + + function onPopoutOpening() { + root.closeMenu(); + } + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + onClicked: root.closeMenu() + } + + Rectangle { + id: menuRect + + x: Math.max(10, Math.min(root.width - width - 10, root.anchorPos.x)) + y: Math.max(10, Math.min(root.height - height - 10, root.anchorPos.y)) + width: 220 + height: menuColumn.implicitHeight + Theme.spacingS * 2 + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium + border.width: BlurService.enabled ? BlurService.borderWidth : 1 + + Column { + id: menuColumn + + anchors.top: parent.top + anchors.topMargin: Theme.spacingS + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - Theme.spacingS * 2 + spacing: 1 + + Repeater { + model: [ + { + "label": I18n.tr("Set notification rules"), + "action": "rules" + }, + { + "label": root.isMuted ? I18n.tr("Unmute popups for %1").arg(root.appName || I18n.tr("this app")) : I18n.tr("Mute popups for %1").arg(root.appName || I18n.tr("this app")), + "action": "mute" + }, + { + "label": I18n.tr("Dismiss"), + "action": "dismiss" + } + ] + + Rectangle { + id: menuRow + + required property var modelData + + width: parent.width + height: 32 + radius: Theme.cornerRadius / 2 + color: rowArea.containsMouse ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0) + + StyledText { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + text: menuRow.modelData.label + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + elide: Text.ElideRight + } + + MouseArea { + id: rowArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: root.triggerAction(menuRow.modelData.action) + } + } + } + } + } +} diff --git a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml index a3187d79e..814f8972d 100644 --- a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml +++ b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml @@ -4,6 +4,7 @@ import Quickshell import Quickshell.Wayland import Quickshell.Services.Notifications import qs.Common +import qs.Modules.Notifications import qs.Services import qs.Widgets @@ -1132,7 +1133,12 @@ PanelWindow { if (!notificationData || win.exiting) return; if (mouse.button === Qt.RightButton) { - popupContextMenu.popup(); + popupContextMenuLoader.active = true; + const menu = popupContextMenuLoader.item; + if (menu) { + const p = mapToItem(null, mouse.x, mouse.y); + menu.showAt(win.margins.left + p.x, win.margins.top + p.y, win.screen); + } } else if (mouse.button === Qt.LeftButton) { const canExpand = bodyText.hasMoreText || win.descriptionExpanded || (SettingsData.notificationPopupPrivacyMode && win.hasExpandableBody); if (canExpand) { @@ -1376,95 +1382,19 @@ PanelWindow { } } - Menu { - id: popupContextMenu - width: 220 - contentHeight: 130 - margins: -1 - popupType: Popup.Window - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + Loader { + id: popupContextMenuLoader + active: false - background: Rectangle { - color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) - radius: Theme.cornerRadius - border.width: 0 - border.color: Theme.outlineStrong - } - - MenuItem { - id: setNotificationRulesItem - text: I18n.tr("Set notification rules") - - contentItem: StyledText { - text: parent.text - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - leftPadding: Theme.spacingS - verticalAlignment: Text.AlignVCenter + sourceComponent: NotificationContextMenu { + appName: notificationData?.appName ?? "" + desktopEntry: notificationData?.desktopEntry ?? "" + onMuted: { + if (notificationData && !win.exiting) + NotificationService.dismissNotification(notificationData); } - - background: Rectangle { - color: parent.hovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0) - radius: Theme.cornerRadius / 2 - } - - onTriggered: { - const appName = notificationData?.appName || ""; - const desktopEntry = notificationData?.desktopEntry || ""; - SettingsData.addNotificationRuleForNotification(appName, desktopEntry); - PopoutService.openSettingsWithTab("notifications"); - } - } - - MenuItem { - id: muteUnmuteItem - readonly property bool isMuted: SettingsData.isAppMuted(notificationData?.appName || "", notificationData?.desktopEntry || "") - text: isMuted ? I18n.tr("Unmute popups for %1").arg(notificationData?.appName || I18n.tr("this app")) : I18n.tr("Mute popups for %1").arg(notificationData?.appName || I18n.tr("this app")) - - contentItem: StyledText { - text: parent.text - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - leftPadding: Theme.spacingS - verticalAlignment: Text.AlignVCenter - } - - background: Rectangle { - color: parent.hovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0) - radius: Theme.cornerRadius / 2 - } - - onTriggered: { - const appName = notificationData?.appName || ""; - const desktopEntry = notificationData?.desktopEntry || ""; - if (isMuted) { - SettingsData.removeMuteRuleForApp(appName, desktopEntry); - } else { - SettingsData.addMuteRuleForApp(appName, desktopEntry); - if (notificationData && !exiting) - NotificationService.dismissNotification(notificationData); - } - } - } - - MenuItem { - text: I18n.tr("Dismiss") - - contentItem: StyledText { - text: parent.text - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - leftPadding: Theme.spacingS - verticalAlignment: Text.AlignVCenter - } - - background: Rectangle { - color: parent.hovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0) - radius: Theme.cornerRadius / 2 - } - - onTriggered: { - if (notificationData && !exiting) + onDismissRequested: { + if (notificationData && !win.exiting) NotificationService.dismissNotification(notificationData); } }