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 {