notification daemon + center

This commit is contained in:
Zacharias-Brohn
2025-11-09 01:13:05 +01:00
parent 6d52325d89
commit 176b5c79b7
10 changed files with 956 additions and 254 deletions
+17 -1
View File
@@ -19,6 +19,12 @@ Scope {
property bool trayMenuVisible: false
screen: modelData
Process {
id: ncProcess
command: ["sh", "-c", "qs -p /home/zach/GitProjects/z-bar-qt/shell.qml ipc call root showCenter"]
running: false
}
anchors {
top: true
left: true
@@ -93,8 +99,18 @@ Scope {
Layout.alignment: Qt.AlignVCenter
}
SwayNC {
Text {
Layout.alignment: Qt.AlignVCenter
text: "\ue7f4"
font.family: "Material Symbols Rounded"
font.pixelSize: 22
color: "white"
MouseArea {
anchors.fill: parent
onClicked: {
ncProcess.running = true
}
}
}
}
}
+49
View File
@@ -0,0 +1,49 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
import qs.Modules
Rectangle {
id: root
Text {
text: "\ue7f4"
font.family: "Material Symbols Rounded"
font.pixelSize: 16
color: "white"
anchors.centerIn: parent
}
property list<int> notifIds: []
NotificationServer {
id: notificationServer
imageSupported: true
actionsSupported: true
persistenceSupported: true
bodyImagesSupported: true
bodySupported: true
onNotification: {
notification.tracked = true;
notification.receivedTime = Date.now();
root.notifIds.push(notification.id);
notificationComponent.createObject(root, { notif: notification, visible: !notificationCenter.doNotDisturb });
}
}
Component {
id: notificationComponent
Notification {
centerX: notificationCenter.posX
notifIndex: root.notifIds
onNotifDestroy: {
root.notifIds.shift();
}
}
}
NotificationCenter {
id: notificationCenter
notifications: notificationServer.trackedNotifications.values
}
}
+155
View File
@@ -0,0 +1,155 @@
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import QtQuick.Layouts
import QtQuick
import Quickshell.Services.Notifications
PanelWindow {
id: root
color: "transparent"
anchors {
top: true
right: true
left: true
bottom: true
}
mask: Region { item: backgroundRect }
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay
required property Notification notif
required property int centerX
required property list<int> notifIndex
property int index: notifIndex.indexOf(notif.id)
signal notifDestroy()
Component.onCompleted: {
openAnim.start();
}
Timer {
id: timeout
interval: 5000
onTriggered: {
closeAnim.start();
}
}
NumberAnimation {
id: openAnim
target: backgroundRect
property: "x"
from: root.centerX
to: root.centerX - backgroundRect.implicitWidth - 20
duration: 200
easing.type: Easing.InOutQuad
onStopped: { timeout.start(); }
}
NumberAnimation {
id: closeAnim
target: backgroundRect
property: "x"
from: root.centerX - backgroundRect.implicitWidth - 20
to: root.centerX
duration: 200
easing.type: Easing.InOutQuad
onStopped: {
root.destroy();
root.notifDestroy();
}
}
Rectangle {
id: backgroundRect
implicitWidth: 400
implicitHeight: 80
x: root.centerX - implicitWidth - 20
y: 34 + 20 + ( root.index * ( implicitHeight + 10 ))
color: "#801a1a1a"
border.color: "#555555"
radius: 8
Behavior on y {
NumberAnimation {
duration: 200
easing.type: Easing.InOutQuad
}
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
IconImage {
source: root.notif.image
Layout.preferredWidth: 64
Layout.preferredHeight: 64
Layout.alignment: Qt.AlignHCenter | Qt.AlignLeft
visible: root.notif.image !== ""
}
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: root.notif.image !== "" ? 0 : 16
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Text {
text: root.notif.appName
color: "white"
font.bold: true
font.pointSize: 12
elide: Text.ElideRight
wrapMode: Text.NoWrap
Layout.fillWidth: true
}
Text {
text: root.notif.summary
color: "white"
font.pointSize: 10
elide: Text.ElideRight
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
Text {
text: root.notif.body
color: "#dddddd"
font.pointSize: 8
elide: Text.ElideRight
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
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: {
root.notif.dismiss();
root.visible = false;
}
}
}
}
}
+449
View File
@@ -0,0 +1,449 @@
import Quickshell
import Quickshell.Hyprland
import Quickshell.Widgets
import Quickshell.Io
import QtQuick.Layouts
import QtQuick.Controls.FluentWinUI3
import QtQuick.Effects
import QtQuick
import Quickshell.Services.Notifications
PanelWindow {
id: root
color: "transparent"
anchors {
top: true
right: true
left: true
bottom: true
}
required property list<Notification> notifications
property bool centerShown: false
property alias posX: backgroundRect.x
property alias doNotDisturb: dndSwitch.checked
visible: false
IpcHandler {
id: ipcHandler
target: "root"
function showCenter(): void { root.centerShown = !root.centerShown; }
}
onVisibleChanged: {
if ( root.visible ) {
showAnimation.start();
}
}
onCenterShownChanged: {
if ( !root.centerShown ) {
closeAnimation.start();
closeTimer.start();
} else {
root.visible = true;
}
}
Timer {
id: closeTimer
interval: 300
onTriggered: {
root.visible = false;
}
}
NumberAnimation {
id: showAnimation
target: backgroundRect
property: "x"
from: Screen.width
to: Screen.width - backgroundRect.implicitWidth - 10
duration: 300
easing.type: Easing.OutCubic
}
NumberAnimation {
id: closeAnimation
target: backgroundRect
property: "x"
from: Screen.width - backgroundRect.implicitWidth - 10
to: Screen.width
duration: 300
easing.type: Easing.OutCubic
}
QtObject {
id: groupedData
property var groups: ({})
function updateGroups() {
var newGroups = {};
for ( var i = 0; i < root.notifications.length; i++ ) {
var notif = root.notifications[ i ];
var appName = notif.appName || "Unknown";
if ( !newGroups[ appName ]) {
newGroups[ appName ] = [];
}
newGroups[ appName ].push( notif );
}
// Sort notifications within each group (latest first)
for ( var app in newGroups ) {
newGroups[ app ].sort(( a, b ) => b.receivedTime - a.receivedTime );
}
groups = newGroups;
groupsChanged();
}
Component.onCompleted: updateGroups()
}
Connections {
target: root
function onNotificationsChanged() {
groupedData.updateGroups();
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: {
if ( root.centerShown ) {
root.centerShown = false;
}
}
}
Rectangle {
id: backgroundRect
y: 10
x: Screen.width
implicitWidth: 400
implicitHeight: root.height - 20
color: "#801a1a1a"
radius: 8
border.color: "#555555"
border.width: 1
ColumnLayout {
anchors.fill: parent
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"
}
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 ( var app in groupedData.groups ) {
groupedData.groups[ app ].forEach( function( n ) { n.dismiss(); });
}
}
}
}
}
}
Rectangle {
color: "#333333"
Layout.preferredHeight: 1
Layout.fillWidth: true
}
Flickable {
Layout.fillWidth: true
Layout.fillHeight: true
contentHeight: notificationColumn.implicitHeight
clip: true
Column {
id: notificationColumn
width: parent.width
spacing: 10
add: Transition {
NumberAnimation {
properties: "x";
duration: 300;
easing.type: Easing.OutCubic
}
}
move: Transition {
NumberAnimation {
properties: "y,x";
duration: 200;
easing.type: Easing.OutCubic
}
}
Repeater {
model: Object.keys( groupedData.groups )
Column {
id: groupColumn
required property string modelData
property var notifications: groupedData.groups[ modelData ]
width: parent.width
spacing: 10
property bool shouldShow: false
property bool isExpanded: false
move: Transition {
NumberAnimation {
properties: "y,x";
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.isExpanded = false;
}
}
}
}
Repeater {
model: groupColumn.notifications
Rectangle {
id: groupHeader
required property var modelData
required property var index
width: parent.width
height: groupColumn.isExpanded ? ( modelData.actions.length > 1 ? 130 : 80 ) : 80
color: "#801a1a1a"
border.color: "#555555"
border.width: 1
radius: 8
visible: groupColumn.notifications[0].id === modelData.id || groupColumn.isExpanded
// NumberAnimation {
// id: expandAnim
// target: groupHeader
// property: "y"
// duration: 300
// easing.type: Easing.OutCubic
// from: (( groupHeader.height / 2 ) * index )
// to: (( groupHeader.height + 60 ) * index )
// onStarted: {
// groupColumn.shouldShow = true;
// }
// }
//
// NumberAnimation {
// id: collapseAnim
// target: groupHeader
// property: "y"
// duration: 300
// easing.type: Easing.OutCubic
// from: (( groupHeader.height + 60 ) * index )
// to: (( groupHeader.height / 2 ) * index )
// onStopped: {
// groupColumn.isExpanded = false;
// }
// }
Connections {
target: groupColumn
function onShouldShowChanged() {
if ( !shouldShow ) {
// collapseAnim.start();
}
}
}
onVisibleChanged: {
if ( visible ) {
// expandAnim.start();
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if ( groupColumn.isExpanded ) {
if ( groupHeader.modelData.actions.length === 1 ) {
groupHeader.modelData.actions[0].invoke();
}
} else {
groupColumn.isExpanded = true;
}
}
}
Column {
anchors.fill: parent
anchors.margins: 10
RowLayout {
height: 80
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
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 4
Text {
text: groupHeader.modelData.summary
color: "white"
font.bold: true
font.pointSize: 12
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: groupHeader.modelData.body
font.pointSize: 10
color: "#dddddd"
elide: Text.ElideRight
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 {
spacing: 2
visible: groupColumn.isExpanded && groupHeader.modelData.actions.length > 1
height: 15
width: parent.width
Repeater {
model: groupHeader.modelData.actions
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 30
required property var modelData
color: buttonArea.containsMouse ? "#15FFFFFF" : "#09FFFFFF"
radius: 4
Text {
anchors.centerIn: parent
text: modelData.text
color: "white"
font.pointSize: 12
}
MouseArea {
id: buttonArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
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 ? groupColumn.notifications[0].dismiss() : groupColumn.notifications.forEach( function( n ) { n.dismiss(); });
}
}
}
}
}
}
}
}
}
}
}
}
+1 -1
View File
@@ -59,8 +59,8 @@ MouseArea {
if ( mouse.button === Qt.LeftButton ) {
root.item.activate();
} else if ( mouse.button === Qt.RightButton ) {
trayMenu.trayMenu = root.item?.menu;
trayMenu.visible = !trayMenu.visible;
trayMenu.trayMenu = root.item?.menu;
trayMenu.focusGrab = true;
}
}
+3
View File
@@ -59,6 +59,9 @@ PanelWindow {
if ( visible ) {
scaleValue = 0;
scaleAnimation.start();
} else {
root.menuStack.pop();
backEntry.visible = false;
}
}
+280 -251
View File
@@ -1,5 +1,3 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Hyprland
import Quickshell.Widgets
@@ -23,7 +21,6 @@ PanelWindow {
property bool centerShown: false
property alias posX: backgroundRect.x
property alias doNotDisturb: dndSwitch.checked
property alias groupedData: groupedData
visible: false
IpcHandler {
@@ -76,94 +73,35 @@ PanelWindow {
easing.type: Easing.OutCubic
}
ListModel {
QtObject {
id: groupedData
property int totalCount: 0
property var groupMap: ({})
property var groups: ({})
function mapGroups() {
groupedData.groupMap = {};
for ( var i = 0; i < groupedData.count; i++ ) {
var name = get(i).name;
groupMap[ name ] = i;
}
}
function updateCount() {
var count = 0;
for ( var i = 0; i < groupedData.count; i++ ) {
count += get(i).notifications.count;
}
totalCount = count;
}
function ensureGroup(appName) {
for ( var i = 0; i < count; i++ ) {
if ( get(i).name === appName ) {
return get(i).notifications;
function updateGroups() {
var newGroups = {};
for ( var i = 0; i < root.notifications.length; i++ ) {
var notif = root.notifications[ i ];
var appName = notif.appName || "Unknown";
if ( !newGroups[ appName ]) {
newGroups[ appName ] = [];
}
newGroups[ appName ].push( notif );
}
var model = Qt.createQmlObject('import QtQuick 2.0; ListModel {}', root);
append({ name: appName, notifications: model });
mapGroups();
return model;
}
function addNotification(notif) {
var appName = notif.appName
var model = ensureGroup(appName);
model.insert(0, notif);
updateCount();
}
function removeNotification(notif) {
var appName = notif.appName || "Unknown";
var group = get( groupMap[ appName ]);
if ( group.name === appName ) {
root.notifications[ idMap[ notif.id ]].dismiss();
if ( group.notifications.count === 0 ) {
remove( groupMap[ appName ], 1 );
mapGroups();
}
// Sort notifications within each group (latest first)
for ( var app in newGroups ) {
newGroups[ app ].sort(( a, b ) => b.receivedTime - a.receivedTime );
}
updateCount();
groups = newGroups;
groupsChanged();
}
function removeGroup(notif) {
var appName = notif.appName || "Unknown";
var group = get(groupMap[ appName ]);
if ( group.name === appName ) {
for ( var i = 0; i < group.notifications.count; i++ ) {
var item = group.notifications.get( i );
item.dismiss();
}
remove( groupMap[ appName ], 1 );
updateCount();
mapGroups();
}
}
function resetGroups(notifications) {
groupedData.clear();
for (var i = 0; i < root.notifications.length; i++) {
addNotification(root.notifications[i]);
}
updateCount();
}
Component.onCompleted: {
resetGroups(root.notifications)
}
Component.onCompleted: updateGroups()
}
Connections {
target: root
function onNotificationsChanged() {
if ( root.notifications.length > groupedData.totalCount ) {
groupedData.addNotification( root.notifications[ root.notifications.length - 1 ] );
groupedData.mapGroups();
console.log(root.notifications)
}
groupedData.updateGroups();
}
}
@@ -173,8 +111,6 @@ PanelWindow {
onClicked: {
if ( root.centerShown ) {
root.centerShown = false;
console.log("groups", groupedData.count);
console.log(root.notifications)
}
}
}
@@ -189,29 +125,51 @@ PanelWindow {
radius: 8
border.color: "#555555"
border.width: 1
clip: true
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
RowLayout {
Layout.preferredHeight: 30
Layout.fillWidth: true
Text {
text: "Notifications"
color: "white"
font.bold: true
font.pointSize: 16
Layout.fillWidth: true
}
Switch {
id: dndSwitch
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
text: "Do Not Disturb"
}
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 ( var app in groupedData.groups ) {
groupedData.groups[ app ].forEach( function( n ) { n.dismiss(); });
}
}
}
}
}
}
Rectangle {
@@ -220,194 +178,265 @@ PanelWindow {
Layout.fillWidth: true
}
Column {
id: notificationColumn
Layout.fillHeight: true
Flickable {
Layout.fillWidth: true
// width: mainLayout.width
spacing: 10
Layout.fillHeight: true
contentHeight: notificationColumn.implicitHeight
clip: true
move: Transition {
NumberAnimation {
properties: "y,x"
duration: 200
easing.type: Easing.InOutQuad
}
}
add: Transition {
NumberAnimation {
properties: "y,x"
duration: 200
easing.type: Easing.InOutQuad
}
}
ListView {
id: groupListView
model: groupedData
Column {
id: notificationColumn
width: parent.width
spacing: 10
width: 400
height: Math.min(contentHeight, notificationColumn.height)
// Layout.fillWidth: true
// Layout.fillHeight: true
delegate: ListView {
required property var modelData
required property int index
property bool isExpanded: false
property bool isExpandedAnim: false
id: listView
visible: true
property ListModel notificationsModel: modelData.notifications
model: notificationsModel
width: notificationColumn.width
implicitHeight: listView.isExpandedAnim ? contentHeight : 80
clip: true
spacing: 10
onIsExpandedChanged: {
if ( !isExpanded ) {
collapseAnim.start();
} else {
expandAnim.start();
}
}
add: Transition {
NumberAnimation {
id: collapseAnim
target: listView
property: "implicitHeight"
to: 80
from: listView.contentHeight
duration: 80
easing.type: Easing.InOutQuad
onStopped: {
listView.isExpandedAnim = listView.isExpanded;
}
properties: "x";
duration: 300;
easing.type: Easing.OutCubic
}
}
move: Transition {
NumberAnimation {
id: expandAnim
target: listView
property: "implicitHeight"
from: 80
to: listView.contentHeight
duration: 80
easing.type: Easing.InOutQuad
onStopped: {
listView.isExpandedAnim = listView.isExpanded;
}
properties: "y,x";
duration: 200;
easing.type: Easing.OutCubic
}
}
Behavior on y {
NumberAnimation {
duration: 200
easing.type: Easing.InOutQuad
}
}
Repeater {
model: Object.keys( groupedData.groups )
Column {
id: groupColumn
required property string modelData
property var notifications: groupedData.groups[ modelData ]
width: parent.width
spacing: 10
displaced: Transition {
NumberAnimation {
properties: "y,x"
duration: 200
easing.type: Easing.InOutQuad
}
}
property bool shouldShow: false
property bool isExpanded: false
delegate: Rectangle {
id: notificationItem
required property var modelData
required property int index
width: listView.width
height: 80
color: "#801a1a1a"
border.color: "#555555"
border.width: 1
radius: 8
clip: true
visible: true
opacity: 1
MouseArea {
anchors.fill: parent
onClicked: {
listView.isExpanded ? ( notificationItem.index === 0 ? listView.isExpanded = false : null ) : listView.isExpanded = true;
move: Transition {
NumberAnimation {
properties: "y,x";
duration: 100;
easing.type: Easing.OutCubic
}
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 10
width: parent.width
height: 30
IconImage {
source: notificationItem.modelData.image
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Text {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.leftMargin: 5
text: groupColumn.modelData
color: "white"
font.pointSize: 14
font.bold: true
}
ColumnLayout {
Layout.fillWidth: true
Rectangle {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.fillHeight: true
spacing: 4
Layout.preferredWidth: 30
color: collapseArea.containsMouse ? "#15FFFFFF" : "transparent"
radius: 4
visible: groupColumn.isExpanded
Text {
text: notificationItem.modelData.appName
anchors.centerIn: parent
text: "\ue944"
font.family: "Material Symbols Rounded"
font.pointSize: 18
color: "white"
font.bold: true
font.pointSize: 12
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: notificationItem.modelData.summary
color: "white"
font.pointSize: 10
elide: Text.ElideRight
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
Text {
text: notificationItem.modelData.body
color: "#dddddd"
font.pointSize: 8
elide: Text.ElideRight
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter
MouseArea {
id: collapseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
groupColumn.isExpanded = false;
}
}
}
}
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
Repeater {
model: groupColumn.notifications
Rectangle {
id: groupHeader
required property var modelData
required property var index
width: parent.width
height: groupColumn.isExpanded ? ( modelData.actions.length > 1 ? 130 : 80 ) : 80
color: "#801a1a1a"
border.color: "#555555"
border.width: 1
radius: 8
visible: groupColumn.notifications[0].id === modelData.id || groupColumn.isExpanded
Text {
anchors.centerIn: parent
text: "✕"
color: closeArea.containsMouse ? "white" : "#888888"
font.pointSize: 12
}
// NumberAnimation {
// id: expandAnim
// target: groupHeader
// property: "y"
// duration: 300
// easing.type: Easing.OutCubic
// from: (( groupHeader.height / 2 ) * index )
// to: (( groupHeader.height + 60 ) * index )
// onStarted: {
// groupColumn.shouldShow = true;
// }
// }
//
// NumberAnimation {
// id: collapseAnim
// target: groupHeader
// property: "y"
// duration: 300
// easing.type: Easing.OutCubic
// from: (( groupHeader.height + 60 ) * index )
// to: (( groupHeader.height / 2 ) * index )
// onStopped: {
// groupColumn.isExpanded = false;
// }
// }
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.notifications[modelData].dismiss();
Connections {
target: groupColumn
function onShouldShowChanged() {
if ( !shouldShow ) {
// collapseAnim.start();
}
}
}
onVisibleChanged: {
if ( visible ) {
// expandAnim.start();
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if ( groupColumn.isExpanded ) {
if ( groupHeader.modelData.actions.length === 1 ) {
groupHeader.modelData.actions[0].invoke();
}
} else {
groupColumn.isExpanded = true;
}
}
}
Column {
anchors.fill: parent
anchors.margins: 10
RowLayout {
height: 80
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
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 4
Text {
text: groupHeader.modelData.summary
color: "white"
font.bold: true
font.pointSize: 12
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: groupHeader.modelData.body
font.pointSize: 10
color: "#dddddd"
elide: Text.ElideRight
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 {
spacing: 2
visible: groupColumn.isExpanded && groupHeader.modelData.actions.length > 1
height: 15
width: parent.width
Repeater {
model: groupHeader.modelData.actions
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 30
required property var modelData
color: buttonArea.containsMouse ? "#15FFFFFF" : "#09FFFFFF"
radius: 4
Text {
anchors.centerIn: parent
text: modelData.text
color: "white"
font.pointSize: 12
}
MouseArea {
id: buttonArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
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 ? groupColumn.notifications[0].dismiss() : groupColumn.notifications.forEach( function( n ) { n.dismiss(); });
}
}
}
}
}
-1
View File
@@ -17,7 +17,6 @@ Scope {
notification.tracked = true;
notification.receivedTime = Date.now();
root.notifIds.push(notification.id);
notificationCenter.groupedData.addNotification(notification);
notificationComponent.createObject(root, { notif: notification, visible: !notificationCenter.doNotDisturb });
}
}
+2
View File
@@ -1,7 +1,9 @@
//@ pragma UseQApplication
import Quickshell
import qs.Modules
Scope {
Bar {}
// Wallpaper {}
NotifServer {}
}