From 459fb94279ae71d1e780aa85a07a8ac4dc0b5fd4 Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Mon, 17 Nov 2025 22:29:13 +0100 Subject: [PATCH] fix clear all button --- Modules/GroupListView.qml | 128 +++++++++ Modules/NotifGroupRepeater.qml | 234 ++++++++++++++++ Modules/NotificationCenter.qml | 397 +-------------------------- Modules/NotificationCenterHeader.qml | 59 ++++ 4 files changed, 423 insertions(+), 395 deletions(-) create mode 100644 Modules/GroupListView.qml create mode 100644 Modules/NotifGroupRepeater.qml create mode 100644 Modules/NotificationCenterHeader.qml diff --git a/Modules/GroupListView.qml b/Modules/GroupListView.qml new file mode 100644 index 0000000..9206433 --- /dev/null +++ b/Modules/GroupListView.qml @@ -0,0 +1,128 @@ +import Quickshell +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts +import qs.Config +import qs.Daemons + +Repeater { + model: ScriptModel { + values: { + const map = new Map(); + for ( const n of NotifServer.notClosed ) + map.set( n.appName, null ); + for ( const n of NotifServer.list ) + map.set( n.appName, null ); + return [ ...map.keys() ]; + } + onValuesChanged: { + root.flagChanged(); + } + } + Column { + id: groupColumn + required property string modelData + property list notifications: NotifServer.list.filter( n => n.appName === modelData ) + width: parent.width + spacing: 10 + + property bool shouldShow: false + property bool isExpanded: false + + function closeAll(): void { + for ( const n of NotifServer.notClosed.filter( n => n.appName === modelData )) + n.close(); + } + + Behavior on height { + Anim {} + } + + Behavior on y { + Anim { + duration: MaterialEasing.expressiveEffectsTime + easing.bezierCurve: MaterialEasing.expressiveEffects + } + } + + add: Transition { + id: addTrans + SequentialAnimation { + PauseAnimation { + duration: ( addTrans.ViewTransition.index - addTrans.ViewTransition.targetIndexes[ 0 ]) * 50 + } + ParallelAnimation { + NumberAnimation { + properties: "y"; + from: addTrans.ViewTransition.destination.y - (height / 2); + to: addTrans.ViewTransition.destination.y; + duration: 100; + easing.type: Easing.OutCubic + } + NumberAnimation { + properties: "opacity"; + from: 0; + to: 1; + duration: 100; + easing.type: Easing.OutCubic + } + NumberAnimation { + properties: "scale"; + from: 0.7; + to: 1.0; + duration: 100 + easing.type: Easing.InOutQuad + } + } + } + } + + move: Transition { + id: moveTrans + NumberAnimation { + properties: "y"; + duration: 100; + easing.type: Easing.OutCubic + } + } + + RowLayout { + width: parent.width + height: 30 + + Text { + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.leftMargin: 5 + text: groupColumn.modelData + color: "white" + font.pointSize: 14 + font.bold: true + } + + Rectangle { + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + Layout.fillHeight: true + Layout.preferredWidth: 30 + color: collapseArea.containsMouse ? "#15FFFFFF" : "transparent" + radius: 4 + visible: groupColumn.isExpanded + Text { + anchors.centerIn: parent + text: "\ue944" + font.family: "Material Symbols Rounded" + font.pointSize: 18 + color: "white" + } + MouseArea { + id: collapseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + groupColumn.shouldShow = false; + } + } + } + } + NotifGroupRepeater { } + } +} diff --git a/Modules/NotifGroupRepeater.qml b/Modules/NotifGroupRepeater.qml new file mode 100644 index 0000000..514acdd --- /dev/null +++ b/Modules/NotifGroupRepeater.qml @@ -0,0 +1,234 @@ +import Quickshell +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts +import qs.Config +import qs.Daemons + +Repeater { + id: groupListView + model: ScriptModel { + id: groupModel + values: groupColumn.isExpanded ? groupColumn.notifications : groupColumn.notifications.slice( 0, 1 ) + } + + Rectangle { + id: groupHeader + required property int index + required property NotifServer.Notif modelData + property alias notifHeight: groupHeader.height + + property bool previewHidden: groupColumn.shouldShow && index > 0 + + width: parent.width + height: contentColumn.height + 20 + color: Config.baseBgColor + border.color: "#555555" + border.width: 1 + radius: 8 + opacity: previewHidden ? 0 : 1.0 + scale: previewHidden ? 0.7 : 1.0 + + Component.onCompleted: modelData.lock(this); + Component.onDestruction: modelData.unlock(this); + + MouseArea { + anchors.fill: parent + onClicked: { + if ( groupColumn.isExpanded || groupColumn.notifications.length === 1 ) { + if ( groupHeader.modelData.actions.length === 1 ) { + groupHeader.modelData.actions[0].invoke(); + } + } else { + groupColumn.shouldShow = true; + groupColumn.isExpanded = true; + } + } + } + + ParallelAnimation { + id: collapseAnim + running: !groupColumn.shouldShow && index > 0 + + Anim { + target: groupHeader + property: "opacity" + duration: 100 + from: 1 + to: index > 0 ? 0 : 1.0 + } + Anim { + target: groupHeader + property: "scale" + duration: 100 + from: 1 + to: index > 0 ? 0.7 : 1.0 + } + onFinished: { + groupColumn.isExpanded = false; + } + } + + ParallelAnimation { + running: groupHeader.modelData.closed + onFinished: groupHeader.modelData.unlock(groupHeader) + + Anim { + target: groupHeader + property: "opacity" + to: 0 + } + Anim { + target: groupHeader + property: "x" + to: groupHeader.x >= 0 ? groupHeader.width : -groupHeader.width + } + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + + Behavior on x { + Anim { + duration: MaterialEasing.expressiveDefaultSpatialTime + easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial + } + } + + Behavior on y { + Anim { + duration: MaterialEasing.expressiveDefaultSpatialTime + easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial + } + } + + Behavior on height { + Anim { + duration: MaterialEasing.expressiveDefaultSpatialTime + easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial + } + } + + Column { + id: contentColumn + anchors.centerIn: parent + width: parent.width - 20 + spacing: 10 + RowLayout { + width: parent.width + spacing: 10 + + IconImage { + source: groupHeader.modelData.image + Layout.preferredWidth: 48 + Layout.preferredHeight: 48 + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Layout.topMargin: 5 + visible: groupHeader.modelData.image !== "" + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 2 + + Text { + text: groupHeader.modelData.summary + color: "white" + font.bold: true + font.pointSize: 16 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: groupHeader.modelData.body + font.pointSize: 12 + color: "#dddddd" + elide: Text.ElideRight + lineHeightMode: Text.FixedHeight + lineHeight: 20 + wrapMode: Text.WordWrap + maximumLineCount: 5 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + Text { + text: groupColumn.notifications.length > 1 ? ( groupColumn.isExpanded ? "" : "(" + groupColumn.notifications.length + ")" ) : "" + font.pointSize: 10 + color: "#666666" + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + } + } + + RowLayout { + id: actionRow + property NotifServer.Notif notif: groupHeader.modelData + spacing: 2 + visible: groupColumn.isExpanded ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : false + height: 30 + width: parent.width + + Repeater { + model: [ ...actionRow.notif.actions ] + Rectangle { + id: actionButton + Layout.fillWidth: true + Layout.preferredHeight: 30 + required property var modelData + color: buttonArea.containsMouse ? "#15FFFFFF" : "#09FFFFFF" + radius: 4 + Text { + anchors.centerIn: parent + text: actionButton.modelData.text + color: "white" + font.pointSize: 12 + } + MouseArea { + id: buttonArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + console.log( groupHeader.modelData.actions ); + actionButton.modelData.invoke(); + } + } + } + } + } + } + Rectangle { + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 6 + anchors.topMargin: 6 + width: 18 + height: 18 + color: closeArea.containsMouse ? "#FF6077" : "transparent" + radius: 9 + + Text { + anchors.centerIn: parent + text: "✕" + color: closeArea.containsMouse ? "white" : "#888888" + font.pointSize: 12 + } + + MouseArea { + id: closeArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + groupColumn.isExpanded ? groupHeader.modelData.close() : groupColumn.closeAll(); + } + } + } + } +} diff --git a/Modules/NotificationCenter.qml b/Modules/NotificationCenter.qml index 646cff0..3e0015a 100644 --- a/Modules/NotificationCenter.qml +++ b/Modules/NotificationCenter.qml @@ -1,13 +1,10 @@ import Quickshell import Quickshell.Hyprland -import Quickshell.Widgets import Quickshell.Io import Quickshell.Wayland import QtQuick.Layouts import QtQuick.Controls.FluentWinUI3 -import QtQuick.Effects import QtQuick -import Quickshell.Services.Notifications import qs.Config import qs.Helpers import qs.Daemons @@ -113,52 +110,7 @@ PanelWindow { anchors.margins: 10 spacing: 10 - RowLayout { - Layout.fillWidth: true - Switch { - id: dndSwitch - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillWidth: true - text: "Do Not Disturb" - focus: false - activeFocusOnTab: false - focusPolicy: Qt.NoFocus - onCheckedChanged: { - NotifServer.dnd = dndSwitch.checked; - } - } - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Text { - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - text: "Clear all" - color: "white" - } - Rectangle { - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - Layout.preferredWidth: 30 - Layout.preferredHeight: 30 - color: clearArea.containsMouse ? "#15FFFFFF" : "transparent" - radius: 4 - Text { - anchors.centerIn: parent - text: "\ue0b8" - font.family: "Material Symbols Rounded" - font.pointSize: 18 - color: "white" - } - MouseArea { - id: clearArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - for ( const n of NotifServer.list ) - n.close(); - } - } - } - } - } + NotificationCenterHeader { } Rectangle { color: "#333333" @@ -194,353 +146,8 @@ PanelWindow { } } - Repeater { - model: ScriptModel { - values: { - const map = new Map(); - for ( const n of NotifServer.notClosed ) - map.set( n.appName, null ); - for ( const n of NotifServer.list ) - map.set( n.appName, null ); - return [ ...map.keys() ]; - } - onValuesChanged: { - root.flagChanged(); - } - } - Column { - id: groupColumn - required property string modelData - property list notifications: NotifServer.list.filter( n => n.appName === modelData ) - width: parent.width - spacing: 10 + GroupListView { } - property bool shouldShow: false - property bool isExpanded: false - - function closeAll(): void { - for ( const n of NotifServer.notClosed.filter( n => n.appName === modelData )) - n.close(); - } - - Behavior on height { - Anim {} - } - - Behavior on y { - Anim { - duration: MaterialEasing.expressiveEffectsTime - easing.bezierCurve: MaterialEasing.expressiveEffects - } - } - - add: Transition { - id: addTrans - SequentialAnimation { - PauseAnimation { - duration: ( addTrans.ViewTransition.index - addTrans.ViewTransition.targetIndexes[ 0 ]) * 50 - } - ParallelAnimation { - NumberAnimation { - properties: "y"; - from: addTrans.ViewTransition.destination.y - (height / 2); - to: addTrans.ViewTransition.destination.y; - duration: 100; - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "opacity"; - from: 0; - to: 1; - duration: 100; - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "scale"; - from: 0.7; - to: 1.0; - duration: 100 - easing.type: Easing.InOutQuad - } - } - } - } - - move: Transition { - id: moveTrans - NumberAnimation { - properties: "y"; - duration: 100; - easing.type: Easing.OutCubic - } - } - - RowLayout { - width: parent.width - height: 30 - - Text { - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - Layout.leftMargin: 5 - text: groupColumn.modelData - color: "white" - font.pointSize: 14 - font.bold: true - } - - Rectangle { - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - Layout.fillHeight: true - Layout.preferredWidth: 30 - color: collapseArea.containsMouse ? "#15FFFFFF" : "transparent" - radius: 4 - visible: groupColumn.isExpanded - Text { - anchors.centerIn: parent - text: "\ue944" - font.family: "Material Symbols Rounded" - font.pointSize: 18 - color: "white" - } - MouseArea { - id: collapseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - groupColumn.shouldShow = false; - } - } - } - } - - Repeater { - id: groupListView - model: ScriptModel { - id: groupModel - values: groupColumn.isExpanded ? groupColumn.notifications : groupColumn.notifications.slice( 0, 1 ) - } - - Rectangle { - id: groupHeader - required property int index - required property NotifServer.Notif modelData - property alias notifHeight: groupHeader.height - - property bool previewHidden: groupColumn.shouldShow && index > 0 - - width: parent.width - height: contentColumn.height + 20 - color: Config.baseBgColor - border.color: "#555555" - border.width: 1 - radius: 8 - opacity: previewHidden ? 0 : 1.0 - scale: previewHidden ? 0.7 : 1.0 - - Component.onCompleted: modelData.lock(this); - Component.onDestruction: modelData.unlock(this); - - MouseArea { - anchors.fill: parent - onClicked: { - if ( groupColumn.isExpanded || groupColumn.notifications.length === 1 ) { - if ( groupHeader.modelData.actions.length === 1 ) { - groupHeader.modelData.actions[0].invoke(); - } - } else { - groupColumn.shouldShow = true; - groupColumn.isExpanded = true; - } - } - } - - ParallelAnimation { - id: collapseAnim - running: !groupColumn.shouldShow && index > 0 - - Anim { - target: groupHeader - property: "opacity" - duration: 100 - from: 1 - to: index > 0 ? 0 : 1.0 - } - Anim { - target: groupHeader - property: "scale" - duration: 100 - from: 1 - to: index > 0 ? 0.7 : 1.0 - } - onFinished: { - groupColumn.isExpanded = false; - } - } - - ParallelAnimation { - running: groupHeader.modelData.closed - onFinished: groupHeader.modelData.unlock(groupHeader) - - Anim { - target: groupHeader - property: "opacity" - to: 0 - } - Anim { - target: groupHeader - property: "x" - to: groupHeader.x >= 0 ? groupHeader.width : -groupHeader.width - } - } - - Behavior on opacity { - Anim {} - } - - Behavior on scale { - Anim {} - } - - Behavior on x { - Anim { - duration: MaterialEasing.expressiveDefaultSpatialTime - easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial - } - } - - Behavior on y { - Anim { - duration: MaterialEasing.expressiveDefaultSpatialTime - easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial - } - } - - Column { - id: contentColumn - anchors.centerIn: parent - width: parent.width - 20 - spacing: 10 - // anchors.left: parent.left - // anchors.right: parent.right - // anchors.leftMargin: 10 - // anchors.rightMargin: 10 - RowLayout { - width: parent.width - spacing: 10 - - IconImage { - source: groupHeader.modelData.image - Layout.preferredWidth: 48 - Layout.preferredHeight: 48 - Layout.alignment: Qt.AlignTop | Qt.AlignLeft - Layout.topMargin: 5 - visible: groupHeader.modelData.image !== "" - } - - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - spacing: 2 - - Text { - text: groupHeader.modelData.summary - color: "white" - font.bold: true - font.pointSize: 16 - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: groupHeader.modelData.body - font.pointSize: 12 - color: "#dddddd" - elide: Text.ElideRight - lineHeightMode: Text.FixedHeight - lineHeight: 20 - wrapMode: Text.WordWrap - maximumLineCount: 5 - Layout.fillWidth: true - Layout.fillHeight: true - } - } - - Text { - text: groupColumn.notifications.length > 1 ? ( groupColumn.isExpanded ? "" : "(" + groupColumn.notifications.length + ")" ) : "" - font.pointSize: 10 - color: "#666666" - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - } - - } - - RowLayout { - id: actionRow - property NotifServer.Notif notif: groupHeader.modelData - spacing: 2 - // visible: groupColumn.isExpanded ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : ( groupColumn.notifications.length === 1 ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : false ) - visible: true - height: 30 - width: parent.width - - Repeater { - model: [ ...actionRow.notif.actions ] - Rectangle { - id: actionButton - Layout.fillWidth: true - Layout.preferredHeight: 30 - required property var modelData - color: buttonArea.containsMouse ? "#15FFFFFF" : "#09FFFFFF" - radius: 4 - Text { - anchors.centerIn: parent - text: actionButton.modelData.text - color: "white" - font.pointSize: 12 - } - MouseArea { - id: buttonArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - console.log( groupHeader.modelData.actions ); - actionButton.modelData.invoke(); - } - } - } - } - } - } - Rectangle { - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 6 - anchors.topMargin: 6 - width: 18 - height: 18 - color: closeArea.containsMouse ? "#FF6077" : "transparent" - radius: 9 - - Text { - anchors.centerIn: parent - text: "✕" - color: closeArea.containsMouse ? "white" : "#888888" - font.pointSize: 12 - } - - MouseArea { - id: closeArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - groupColumn.isExpanded ? groupHeader.modelData.close() : groupColumn.closeAll(); - } - } - } - } - } - } - } } } } diff --git a/Modules/NotificationCenterHeader.qml b/Modules/NotificationCenterHeader.qml new file mode 100644 index 0000000..27bf814 --- /dev/null +++ b/Modules/NotificationCenterHeader.qml @@ -0,0 +1,59 @@ +import Quickshell +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls.FluentWinUI3 +import qs.Daemons + +RowLayout { + Layout.fillWidth: true + + Switch { + id: dndSwitch + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + text: "Do Not Disturb" + focus: false + activeFocusOnTab: false + focusPolicy: Qt.NoFocus + onCheckedChanged: { + NotifServer.dnd = dndSwitch.checked; + } + } + + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + Text { + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + text: "Clear all" + color: "white" + } + + Rectangle { + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + Layout.preferredWidth: 30 + Layout.preferredHeight: 30 + color: clearArea.containsMouse ? "#15FFFFFF" : "transparent" + radius: 4 + + Text { + anchors.centerIn: parent + text: "\ue0b8" + font.family: "Material Symbols Rounded" + font.pointSize: 18 + color: "white" + } + + MouseArea { + id: clearArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + for ( const n of NotifServer.list.filter( n => !n.closed )) + n.close(); + } + } + } + } +}