From 3677873a05069d398e5b2bd2325f02de75d94ad3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 19 Apr 2025 21:38:33 +0200 Subject: [PATCH] notif actions --- .../quickshell/modules/common/Appearance.qml | 4 +- .../widgets/NotificationActionButton.qml | 34 +++++++++ .../common/widgets/NotificationWidget.qml | 69 +++++++++++++++---- .config/quickshell/services/Notifications.qml | 19 ++++- 4 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 .config/quickshell/modules/common/widgets/NotificationActionButton.qml diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index dde99a649..05c3f059c 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -127,8 +127,10 @@ Singleton { property color colPrimaryContainerActive: mix(m3colors.m3primaryContainer, colLayer1Active, 0.6) property color colSecondaryHover: mix(m3colors.m3secondary, colLayer1Hover, 0.85) property color colSecondaryActive: mix(m3colors.m3secondary, colLayer1Active, 0.4) - property color colSecondaryContainerHover: mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.8) + property color colSecondaryContainerHover: mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.67) property color colSecondaryContainerActive: mix(m3colors.m3secondaryContainer, colLayer1Active, 0.6) + property color colSurfaceContainerHighestHover: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.9) + property color colSurfaceContainerHighestActive: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.82) property color colTooltip: m3colors.m3inverseSurface property color colOnTooltip: m3colors.m3inverseOnSurface property color colScrim: transparentize(m3colors.m3scrim, 0.5) diff --git a/.config/quickshell/modules/common/widgets/NotificationActionButton.qml b/.config/quickshell/modules/common/widgets/NotificationActionButton.qml new file mode 100644 index 000000000..16bdf0a94 --- /dev/null +++ b/.config/quickshell/modules/common/widgets/NotificationActionButton.qml @@ -0,0 +1,34 @@ +import "root:/modules/common" +import "root:/services" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.Notifications + +Button { + id: button + property string buttonText + property string urgency + + implicitHeight: 30 + leftPadding: 10 + rightPadding: 10 + + background: Rectangle { + radius: Appearance.rounding.small + color: (urgency == NotificationUrgency.Critical) ? + (button.down ? Appearance.colors.colSecondaryContainerActive : + button.hovered ? Appearance.colors.colSecondaryContainerHover : + Appearance.m3colors.m3secondaryContainer) : + (button.down ? Appearance.colors.colSurfaceContainerHighestActive : + button.hovered ? Appearance.colors.colSurfaceContainerHighestHover : + Appearance.m3colors.m3surfaceContainerHighest) + } + + contentItem: StyledText { + horizontalAlignment: Text.AlignHCenter + text: buttonText + color: (urgency == NotificationUrgency.Critical) ? Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface + } +} \ No newline at end of file diff --git a/.config/quickshell/modules/common/widgets/NotificationWidget.qml b/.config/quickshell/modules/common/widgets/NotificationWidget.qml index 160501410..aeb637356 100644 --- a/.config/quickshell/modules/common/widgets/NotificationWidget.qml +++ b/.config/quickshell/modules/common/widgets/NotificationWidget.qml @@ -24,8 +24,8 @@ Item { Behavior on implicitHeight { enabled: enableAnimation NumberAnimation { - duration: Appearance.animation.elementDecel.duration - easing.type: Appearance.animation.elementDecel.type + duration: Appearance.animation.elementDecelFast.duration + easing.type: Appearance.animation.elementDecelFast.type } } @@ -42,7 +42,7 @@ Item { Timer { id: destroyTimer - interval: Appearance.animation.elementDecel.duration + interval: Appearance.animation.elementDecelFast.duration repeat: false onTriggered: { root.destroy() @@ -51,11 +51,12 @@ Item { MouseArea { // Middle click to close anchors.fill: parent - acceptedButtons: Qt.MiddleButton + acceptedButtons: Qt.MiddleButton | Qt.RightButton onClicked: (mouse) => { - if (mouse.button == Qt.MiddleButton) { - Notifications.discardNotification(notificationObject.id) - } + if (mouse.button == Qt.MiddleButton) + Notifications.discardNotification(notificationObject.id); + else if (mouse.button == Qt.RightButton) + root.expanded = !root.expanded; } } @@ -64,7 +65,7 @@ Item { anchors.fill: parent anchors.topMargin: notificationListSpacing color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3secondaryContainer : Appearance.colors.colLayer2 + Appearance.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) : Appearance.colors.colLayer2 radius: Appearance.rounding.normal } @@ -92,14 +93,15 @@ Item { Layout.alignment: Qt.AlignTop Layout.fillWidth: false radius: Appearance.rounding.full - color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3secondary : Appearance.m3colors.m3secondaryContainer + color: Appearance.m3colors.m3secondaryContainer MaterialSymbol { visible: notificationObject.appIcon == "" - text: NotificationUtils.guessMessageType(notificationObject.summary) + text: (notificationObject.urgency == NotificationUrgency.Critical) ? "release_alert" : + NotificationUtils.guessMessageType(notificationObject.summary) anchors.fill: parent color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3onSecondary : Appearance.m3colors.m3onSecondaryContainer + Appearance.mix(Appearance.m3colors.m3onSecondary, Appearance.m3colors.m3onSecondaryContainer, 0.1) : + Appearance.m3colors.m3onSecondaryContainer font.pixelSize: 27 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -192,17 +194,56 @@ Item { StyledText { // Notification body Layout.fillWidth: true - Layout.bottomMargin: 10 Layout.leftMargin: 10 Layout.rightMargin: 10 + Layout.bottomMargin: 10 clip: true + wrapMode: expanded ? Text.Wrap : Text.NoWrap elide: Text.ElideRight font.pixelSize: Appearance.font.pixelSize.small horizontalAlignment: Text.AlignLeft color: Appearance.m3colors.m3outline // textFormat: Text.MarkdownText - text: notificationObject.body + text: notificationObject.body + } + + Flickable { + Layout.fillWidth: true + Layout.topMargin: -5 + Layout.leftMargin: 10 + Layout.rightMargin: 10 + Layout.bottomMargin: 10 + visible: expanded + implicitHeight: actionRowLayout.implicitHeight + contentWidth: actionRowLayout.implicitWidth + + RowLayout { // Actions + id: actionRowLayout + + Repeater { + id: actionRepeater + model: notificationObject.actions + NotificationActionButton { + Layout.fillWidth: true + buttonText: modelData.text + urgency: notificationObject.urgency + onClicked: { + Notifications.attemptInvokeAction(notificationObject.id, modelData.identifier); + } + } + } + + NotificationActionButton { + Layout.fillWidth: true + buttonText: "Close" + urgency: notificationObject.urgency + onClicked: { + Notifications.discardNotification(notificationObject.id); + } + } + + } } } } diff --git a/.config/quickshell/services/Notifications.qml b/.config/quickshell/services/Notifications.qml index 3d0cf1dd8..5c315d69a 100644 --- a/.config/quickshell/services/Notifications.qml +++ b/.config/quickshell/services/Notifications.qml @@ -19,7 +19,7 @@ Singleton { NotificationServer { id: notifServer // actionIconsSupported: true - // actionsSupported: true + actionsSupported: true bodyHyperlinksSupported: true // bodyImagesSupported: true bodyMarkupSupported: true @@ -32,7 +32,12 @@ Singleton { notification.tracked = true const newNotifObject = { "id": notification.id, - "actions": [], + "actions": notification.actions.map((action) => { + return { + "identifier": action.identifier, + "text": action.text, + } + }), "appIcon": notification.appIcon, "appName": notification.appName, "body": notification.body, @@ -60,6 +65,16 @@ Singleton { root.discard(id); } + function attemptInvokeAction(id, notifIdentifier) { + const notifServerIndex = notifServer.trackedNotifications.values.findIndex((notif) => notif.id === id); + if (notifServerIndex !== -1) { + const notifServerNotif = notifServer.trackedNotifications.values[notifServerIndex]; + const action = notifServerNotif.actions.find((action) => action.identifier === notifIdentifier); + action.invoke() + } else console.log("Notification not found in server: " + id) + root.discard(id); + } + function triggerListChange() { root.list = root.list.slice(0) }