From 2e8ab6a7417a307846bae429430970fd262b1ca7 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Sun, 24 May 2026 15:48:49 +0200 Subject: [PATCH] Fix convergence notifications Filter @other out of the history blacklist so senders without a\ndesktop entry still land in the drawer. Keep the convergence empty\nstate, dismiss path, and popup geometry aligned with the current\nframe and dock layout. --- .../private/NotificationDrawer.qml | 19 ++++++-- .../mobileshell/qml/components/BaseItem.qml | 4 +- .../notifications/NotificationPopup.qml | 3 +- .../NotificationPopupManager.qml | 16 +++---- .../notifications/NotificationCard.qml | 4 +- .../notifications/NotificationPopupItem.qml | 13 +++++- .../notifications/NotificationsWidget.qml | 46 +++++++++++-------- containments/panel/qml/StatusPanel.qml | 27 ++++++----- 8 files changed, 84 insertions(+), 48 deletions(-) diff --git a/components/mobileshell/qml/actiondrawer/private/NotificationDrawer.qml b/components/mobileshell/qml/actiondrawer/private/NotificationDrawer.qml index 8a367faa..fd046393 100644 --- a/components/mobileshell/qml/actiondrawer/private/NotificationDrawer.qml +++ b/components/mobileshell/qml/actiondrawer/private/NotificationDrawer.qml @@ -46,8 +46,15 @@ Item { height: { let toolH = toolButtonsItem ? toolButtonsItem.height : 0; - let h = Math.min(actionDrawer.height - toolH, notificationWidget.listView.contentHeight + Kirigami.Units.largeSpacing + topMargin); - return maximumHeight > 0 ? Math.min(h, maximumHeight) : h; + let avail = actionDrawer.height - toolH; + let content = notificationWidget.listView.contentHeight + Kirigami.Units.largeSpacing + topMargin; + // When empty, reserve the external cap so the placeholder has room; + // when populated, hug content so the clear-all toolbar sits below the list. + if (maximumHeight > 0 && !hasNotifications) { + return Math.min(avail, maximumHeight); + } + let cap = maximumHeight > 0 ? Math.min(avail, maximumHeight) : avail; + return Math.min(cap, content); } // time source for the time and date whenin landscape mode @@ -63,10 +70,11 @@ Item { // margin adjusted to fit and position into the action drawer MobileShell.NotificationsWidget { id: notificationWidget + readonly property bool isConvergence: ShellSettings.Settings.convergenceModeEnabled anchors.fill: parent - anchors.topMargin: root.topMargin - anchors.rightMargin: actionDrawer.mode == MobileShell.ActionDrawer.Portrait ? 0 : Math.max(root.width - Kirigami.Units.gridUnit * 25, 0) - anchors.leftMargin: actionDrawer.mode == MobileShell.ActionDrawer.Portrait ? 0 : -Kirigami.Units.gridUnit + anchors.topMargin: root.topMargin + (isConvergence ? Kirigami.Units.gridUnit : 0) + anchors.rightMargin: actionDrawer.mode == MobileShell.ActionDrawer.Portrait ? 0 : (isConvergence ? Kirigami.Units.gridUnit : Math.max(root.width - Kirigami.Units.gridUnit * 25, 0)) + anchors.leftMargin: actionDrawer.mode == MobileShell.ActionDrawer.Portrait ? 0 : (isConvergence ? Kirigami.Units.gridUnit : -Kirigami.Units.gridUnit) historyModel: actionDrawer.notificationModel historyModelType: actionDrawer.notificationModelType @@ -75,6 +83,7 @@ Item { onUnlockRequested: actionDrawer.permissionsRequested() topPadding: root.topPadding showHeader: actionDrawer.mode != MobileShell.ActionDrawer.Portrait + emptyText: ShellSettings.Settings.convergenceModeEnabled ? i18n("No notifications") : "" listView.interactive: !actionDrawer.dragging && root.listOverflowing cardColorScheme: Kirigami.Theme.View diff --git a/components/mobileshell/qml/components/BaseItem.qml b/components/mobileshell/qml/components/BaseItem.qml index 59ec1a72..88d96cd9 100644 --- a/components/mobileshell/qml/components/BaseItem.qml +++ b/components/mobileshell/qml/components/BaseItem.qml @@ -27,8 +27,8 @@ Item { property Item contentItem: Item {} property Item background: Item {} - implicitHeight: topPadding + bottomPadding + contentItem.implicitHeight - implicitWidth: leftPadding + rightPadding + contentItem.implicitWidth + implicitHeight: topPadding + bottomPadding + (contentItem ? contentItem.implicitHeight : 0) + implicitWidth: leftPadding + rightPadding + (contentItem ? contentItem.implicitWidth : 0) onContentItemChanged: { if (contentItem !== null && contentItem !== undefined) { diff --git a/components/mobileshell/qml/popups/notifications/NotificationPopup.qml b/components/mobileshell/qml/popups/notifications/NotificationPopup.qml index ee3741ae..db2487c5 100644 --- a/components/mobileshell/qml/popups/notifications/NotificationPopup.qml +++ b/components/mobileshell/qml/popups/notifications/NotificationPopup.qml @@ -27,6 +27,7 @@ Item { // 'popupWidth' and 'openOffset' is set by the 'notificationPopupManager' property int popupWidth property real openOffset + property real convergenceBottomInset: openOffset property bool isConvergence: false readonly property int primaryAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) * 1.5) readonly property int secondaryAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) @@ -35,7 +36,7 @@ Item { // In convergence the popup enters from the bottom-right corner readonly property real effectiveOpenOffset: isConvergence - ? (Screen.height - openOffset - popupHeight) + ? (Screen.height - convergenceBottomInset - popupHeight) : openOffset readonly property real effectiveClosedOffset: isConvergence ? (Screen.height + Kirigami.Units.smallSpacing) diff --git a/components/mobileshell/qml/popups/notifications/NotificationPopupManager.qml b/components/mobileshell/qml/popups/notifications/NotificationPopupManager.qml index 3b13f952..19f1399f 100644 --- a/components/mobileshell/qml/popups/notifications/NotificationPopupManager.qml +++ b/components/mobileshell/qml/popups/notifications/NotificationPopupManager.qml @@ -33,9 +33,8 @@ Window { readonly property real openOffset: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3 readonly property int longestLength: Math.max(Screen.width, Screen.height) readonly property bool isConvergence: ShellSettings.Settings.convergenceModeEnabled - // Margin between popup and screen edge in convergence mode; used in both - // the delegate x position and the input-region calculation so they stay in sync. - readonly property real convergencePopupMargin: Kirigami.Units.gridUnit * 2 + readonly property real convergencePopupMargin: MobileShell.Constants.convergenceWorkspaceFrameThickness + Kirigami.Units.largeSpacing + readonly property real convergencePopupBottomInset: MobileShell.Constants.convergenceDockHeight + MobileShell.Constants.convergenceWorkspaceFrameThickness + Kirigami.Units.largeSpacing readonly property int popupAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) * 1.5) property var keyboardInteractivity: LayerShell.Window.KeyboardInteractivityNone @@ -104,9 +103,9 @@ Window { } if (isConvergence) { - let regionX = notificationPopupManager.width - notificationPopupManager.popupWidth - notificationPopupManager.convergencePopupMargin; - let regionY = openOffset; - ShellUtil.setInputRegion(notificationPopupManager, Qt.rect(regionX, regionY, notificationPopupManager.popupWidth + Kirigami.Units.gridUnit * 2, popupHeight + Kirigami.Units.gridUnit * 2)); + let regionX = notificationPopupManager.width - notificationPopupManager.popupWidth - notificationPopupManager.convergencePopupMargin - Kirigami.Units.gridUnit / 2; + let regionY = (currentPopup ? currentPopup.effectiveOpenOffset : Screen.height - notificationPopupManager.convergencePopupBottomInset - popupHeight) - Kirigami.Units.gridUnit / 2; + ShellUtil.setInputRegion(notificationPopupManager, Qt.rect(regionX, regionY, notificationPopupManager.popupWidth + Kirigami.Units.gridUnit, popupHeight + Kirigami.Units.gridUnit)); } else { ShellUtil.setInputRegion(notificationPopupManager, Qt.rect((notificationPopupManager.width - notificationPopupManager.popupWidth - Kirigami.Units.gridUnit) / 2, openOffset - Kirigami.Units.gridUnit / 2, notificationPopupManager.popupWidth + Kirigami.Units.gridUnit, popupHeight + Kirigami.Units.gridUnit * ((notifications.count - notifications.currentPopupIndex > 1) ? 4 : 1))); } @@ -207,13 +206,14 @@ Window { id: popup x: notificationPopupManager.isConvergence - ? (parent.width - width - notificationPopupManager.convergencePopupMargin) - : (parent.width - width) / 2 + ? (notificationPopupManager.width - width - notificationPopupManager.convergencePopupMargin) + : (notificationPopupManager.width - width) / 2 z: notifications.count - index isConvergence: notificationPopupManager.isConvergence popupWidth: notificationPopupManager.popupWidth openOffset: notificationPopupManager.openOffset + convergenceBottomInset: notificationPopupManager.convergencePopupBottomInset keyboardInteractivity: notificationPopupManager.keyboardInteractivity popupNotifications: notifications diff --git a/components/mobileshell/qml/widgets/notifications/NotificationCard.qml b/components/mobileshell/qml/widgets/notifications/NotificationCard.qml index 006c801a..e36ff037 100644 --- a/components/mobileshell/qml/widgets/notifications/NotificationCard.qml +++ b/components/mobileshell/qml/widgets/notifications/NotificationCard.qml @@ -78,10 +78,12 @@ Item { signal dragEnd() onContentItemChanged: { + if (!contentItem) { + return; + } contentItem.parent = contentParent; contentItem.anchors.fill = contentParent; contentItem.anchors.margins = Kirigami.Units.largeSpacing; - contentParent.children.push(contentItem); } implicitHeight: contentParent.implicitHeight diff --git a/components/mobileshell/qml/widgets/notifications/NotificationPopupItem.qml b/components/mobileshell/qml/widgets/notifications/NotificationPopupItem.qml index d7354925..b5f302e9 100644 --- a/components/mobileshell/qml/widgets/notifications/NotificationPopupItem.qml +++ b/components/mobileshell/qml/widgets/notifications/NotificationPopupItem.qml @@ -6,6 +6,7 @@ */ import QtQuick 2.15 +import QtQuick.Controls as Controls import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 @@ -58,7 +59,6 @@ BaseNotificationItem { onDismissRequested: { model.resident = false; notificationItem.dismissRequested(); - notificationItem.close(); } onDragStart: notificationItem.dragStart() @@ -116,6 +116,17 @@ BaseNotificationItem { time: notificationItem.time clockSource: notificationItem.clockSource } + + PlasmaComponents.ToolButton { + visible: notificationItem.closable + icon.name: "window-close" + text: i18n("Dismiss") + display: Controls.AbstractButton.IconOnly + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium + Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium + onClicked: mainCard.dismissRequested() + } } // notification contents diff --git a/components/mobileshell/qml/widgets/notifications/NotificationsWidget.qml b/components/mobileshell/qml/widgets/notifications/NotificationsWidget.qml index e52fe335..2ace9ca9 100644 --- a/components/mobileshell/qml/widgets/notifications/NotificationsWidget.qml +++ b/components/mobileshell/qml/widgets/notifications/NotificationsWidget.qml @@ -86,6 +86,8 @@ Item { */ property bool showHeader: false + property string emptyText: "" + /** * Gives access to the notification list view outside of the notification widget. */ @@ -179,6 +181,32 @@ Item { id: clock } + // Empty-state placeholders centred in the full widget rather than the + // (possibly tiny) ListView so they don't clip out of view. + PlasmaExtras.PlaceholderMessage { + anchors.centerIn: parent + width: parent.width - (Kirigami.Units.gridUnit * 4) + + text: i18n("Notification service not available") + visible: list.count === 0 && !NotificationManager.Server.valid && historyModelType === NotificationsModelType.NotificationsModel + + PlasmaComponents3.Label { + readonly property NotificationManager.ServerInfo currentOwner: !NotificationManager.Server.valid ? NotificationManager.Server.currentOwner : null + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: currentOwner ? i18nc("Vendor and product name", "Notifications are currently provided by '%1 %2'", currentOwner.vendor, currentOwner.name) : "" + visible: currentOwner && currentOwner.vendor && currentOwner.name + } + } + + PlasmaExtras.PlaceholderMessage { + anchors.centerIn: parent + width: parent.width - (Kirigami.Units.gridUnit * 4) + + text: root.emptyText + visible: list.count === 0 && root.emptyText.length > 0 && NotificationManager.Server.valid + } + ListView { id: list model: historyModel @@ -250,24 +278,6 @@ Item { criteria: ViewSection.FullString } - PlasmaExtras.PlaceholderMessage { - anchors.centerIn: parent - width: parent.width - (Kirigami.Units.gridUnit * 4) - - text: i18n("Notification service not available") - visible: list.count === 0 && !NotificationManager.Server.valid && historyModelType === NotificationsModelType.NotificationsModel - - PlasmaComponents3.Label { - // Checking valid to avoid creating ServerInfo object if everything is alright - readonly property NotificationManager.ServerInfo currentOwner: !NotificationManager.Server.valid ? NotificationManager.Server.currentOwner : null - // PlasmaExtras.PlaceholderMessage is internally a ColumnLayout, so we can use Layout.whatever properties here - Layout.fillWidth: true - wrapMode: Text.WordWrap - text: currentOwner ? i18nc("Vendor and product name", "Notifications are currently provided by '%1 %2'", currentOwner.vendor, currentOwner.name) : "" - visible: currentOwner && currentOwner.vendor && currentOwner.name - } - } - // Run every time an item is visually added to the list, thus when `Show n more` button is clicked as well. add: Transition { MobileShell.MotionNumberAnimation { property: "opacity"; from: 0; to: 1; duration: list.animationDuration; type: MobileShell.Motion.Standard } diff --git a/containments/panel/qml/StatusPanel.qml b/containments/panel/qml/StatusPanel.qml index 4914c5f5..f9fe91f8 100644 --- a/containments/panel/qml/StatusPanel.qml +++ b/containments/panel/qml/StatusPanel.qml @@ -183,25 +183,28 @@ Item { actionDrawer.restrictedPermissions: MobileShellState.LockscreenDBusClient.lockscreenActive - actionDrawer.notificationSettings: NotificationManager.Settings {} + actionDrawer.notificationSettings: NotificationManager.Settings { + id: notificationSettings + } actionDrawer.notificationModel: NotificationManager.Notifications { showExpired: true showDismissed: true - showJobs: drawer.actionDrawer.notificationSettings.jobsInNotifications + showJobs: notificationSettings.jobsInNotifications sortMode: NotificationManager.Notifications.SortByTypeAndUrgency groupMode: NotificationManager.Notifications.GroupApplicationsFlat groupLimit: 2 expandUnread: true - blacklistedDesktopEntries: drawer.actionDrawer.notificationSettings.historyBlacklistedApplications - blacklistedNotifyRcNames: drawer.actionDrawer.notificationSettings.historyBlacklistedServices - urgencies: { - var urgencies = NotificationManager.Notifications.CriticalUrgency - | NotificationManager.Notifications.NormalUrgency; - if (drawer.actionDrawer.notificationSettings.lowPriorityHistory) { - urgencies |= NotificationManager.Notifications.LowUrgency; - } - return urgencies; - } + // Strip "@other" from the blacklist: Plasma's default config blocks + // notifications from unregistered/non-configurable sources, which on + // a mobile/convergence shell silently hides anything not shipping a + // .desktop entry (e.g. third-party DBus senders). Shift surfaces all + // notifications and lets the user blacklist individual apps later. + blacklistedDesktopEntries: notificationSettings.historyBlacklistedApplications + .filter(function(e) { return e !== "@other"; }) + blacklistedNotifyRcNames: notificationSettings.historyBlacklistedServices + urgencies: NotificationManager.Notifications.CriticalUrgency + | NotificationManager.Notifications.NormalUrgency + | NotificationManager.Notifications.LowUrgency } Connections {