feat: add notification sound and notification window enhancements. Ready for alpha.NotificationBody needs work

This commit is contained in:
Hannes
2026-08-26 00:38:20 +02:00
parent 0b20e8eb85
commit 3cd6807681
7 changed files with 193 additions and 88 deletions
+1
View File
@@ -3,3 +3,4 @@ Colors.qml
modules/wlogout/scripts/helper.sh
config/path/ConfigPath.qml
config/path/WallpaperPath.qml
modules/notification/assets/notification.ogg
+9
View File
@@ -105,6 +105,15 @@ download \
"$SCRIPT_DIR/modules/jl/assets/correct.svg" \
"correct.svg"
# ----------------------------
# Sounds
# ----------------------------
download \
"https://raw.githubusercontent.com/akx/Notifications/master/OGG/Chord.ogg" \
"$SCRIPT_DIR/modules/notification/assets/notification.ogg" \
"notification.ogg"
# ----------------------------
# Nerd Font
# ----------------------------
@@ -7,20 +7,20 @@ import Quickshell.Services.Pipewire
Item {
id: root
PwObjectTracker {
objects: [ Pipewire.defaultAudioSource ]
}
property var audioSource: Pipewire.defaultAudioSource
property bool exists: audioSource != null
property bool exists: audioSource != null && audioSource.ready
property bool mute: audioSource?.audio?.muted ?? false
property real volume: Math.round((audioSource?.audio?.volume ?? 0) * 1000) / 10
property bool volumeChanged: false
PwObjectTracker {
objects: [ root.audioSource ]
}
Connections {
target: audioSource?.audio
target: root.audioSource?.audio
function onVolumeChanged() {
root.volumeChanged = true;
+46 -15
View File
@@ -5,18 +5,24 @@ import "../../config"
import "./notificationServer"
PanelWindow {
mask: Region {}
// mask: Region {}
id: notificationWindow
anchors.top: true
anchors.left: true
implicitHeight: screen.height/6
implicitWidth: screen.width/6
// implicitHeight: screen.height/6
implicitHeight: contentHeight+(content.spacing*(notificationListModel.count-1))
implicitWidth: contentWidth
color: "transparent"
ListModel {
id: notificationListModel
}
Connections {
target: NotificationS
@@ -25,17 +31,11 @@ PanelWindow {
return
if (NotificationS.correctMonitor(screen)) {
console.log("[Notification]: This notification belongs to me!", screen.x, screen.y)
// currentNotifications.push(notification)
// currentNotifications = currentNotifications
currentNotification = notification
currentNotifications = currentNotifications.concat([notification])
notificationListModel.append({notificationData: notification})
}
}
}
property var currentNotification: null
property var currentNotifications: []
Rectangle {
anchors.fill: parent
@@ -44,22 +44,53 @@ PanelWindow {
color: "transparent"
Column {
id: content
anchors.fill: parent
anchors.margins: 10
spacing: 5
spacing: -2
Repeater {
model: currentNotifications
id: notificationRepeater
model: notificationListModel
delegate: NotificationBody {
required property int index
required property var notificationData
notification: currentNotifications[index]
width: notificationWindow.width
notification: notificationData
onDismissed: function(notification) {
notificationListModel.remove(index)
notification.dismiss()
console.log("[Notification]: Dismissed Notification:", notification.appName, notification.id, notification.summary, notification.body)
}
}
onItemAdded: updateContentSize()
onItemRemoved: updateContentSize()
}
}
}
property real contentWidth: 0
property real contentHeight: 0
function updateContentSize() {
let width = 0
let height = 0
for (let i = 0; i < notificationRepeater.count; ++i) {
let item = notificationRepeater.itemAt(i)
if (item) {
width = Math.max(width, item.width)
height += item.height
}
}
contentWidth = width
contentHeight = height
console.log("[updateContentSize] contentWidth:", contentWidth, "contentHeight:", contentHeight)
}
Component.onCompleted: {
console.log(
"[Notification]:",
+77 -50
View File
@@ -6,7 +6,84 @@ Rectangle {
id: root
required property var notification
property real duration: 5000 // Expected in milliseconds (e.g., 5000 for 5s)
signal dismissed(var notification)
implicitWidth: content.implicitWidth + 2*18
implicitHeight: content.implicitHeight + 18 + progressBar.height
color: Qt.alpha(Colors.primaryContainer, 0.75)
border.width: Config.border_width
border.color: Colors.primary
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
dismissed(root.notification)
}
Column {
id: content
anchors.fill: parent
anchors.margins: 10
spacing: 5
Text {
id: appNameText
text: root.notification?.id + ": " + (root.notification?.appName ?? "")
font.family: Config.font
font.pixelSize: 18
color: Colors.primary
elide: Text.ElideRight
}
Text {
id: summaryText
text: root.notification?.summary ?? ""
font.family: Config.font
font.pixelSize: 22
color: Colors.primary
wrapMode: Text.Wrap
}
Text {
id: bodyText
text: root.notification?.body ?? ""
font.family: Config.font
font.pixelSize: 16
color: Colors.primary
wrapMode: Text.Wrap
}
}
}
// Timeout progress bar
Rectangle {
id: progressBar
anchors.bottom: parent.bottom
anchors.left: parent.left
height: 10
color: Colors.primary
property real progress: 1.0
width: root.width * progress
NumberAnimation on progress {
id: anim
running: root.duration > 0
from: 1.0
to: 0.0
// If your duration property comes in seconds (e.g., 5), change to: (root.duration * 1000)
duration: root.duration
paused: mouseArea.containsMouse
onFinished: root.dismissed(root.notification)
}
}
Component.onCompleted: {
console.log(
@@ -16,54 +93,4 @@ Rectangle {
notification.appName
)
}
implicitWidth: parent ? parent.width : 300
implicitHeight: content.implicitHeight + 20
color: "transparent"
border.width: Config.border_width
border.color: Colors.primary
Column {
id: content
anchors.fill: parent
anchors.margins: 10
spacing: 5
Text {
width: parent.width
text: root.notification?.appName ?? ""
font.family: Config.font
font.pixelSize: 18
color: Colors.primary
elide: Text.ElideRight
}
Text {
width: parent.width
text: root.notification?.summary ?? ""
font.family: Config.font
font.pixelSize: 22
color: Colors.primary
wrapMode: Text.Wrap
}
Text {
width: parent.width
text: root.notification?.body ?? ""
font.family: Config.font
font.pixelSize: 16
color: Colors.primary
wrapMode: Text.Wrap
}
}
}
@@ -2,15 +2,16 @@ pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.Services.Notifications
import "../../../config"
Item {
id: notificationServerState
property var focusedMonitor: null
property var newestNotification: null
property var trackedNotifications: [] // TODO
signal notificationReceived(var notification)
@@ -18,33 +19,69 @@ Item {
return (Hyprland.monitorFor(screen) === focusedMonitor)
}
Process {
id: notificationSound
command: ["pw-play", "--volume", "0.25", Config.configDir + "/modules/notification/assets/notification.ogg"]
function play() {
if (running)
return
console.log(
"[NotificationSound] Playing:", Config.configDir + "/modules/notification/assets/notification.ogg"
)
notificationSound.startDetached()
}
}
NotificationServer {
id: notificationServer
onNotification: function(notification) {
if(notification.lastGeneration){
console.log(
"[NotificationServer] Closing Notification:",
notification.appName, notification.id, notification.summary, notification.body
)
notification.dismiss()
return
}
// if(notification.lastGeneration){
// console.log(
// "[NotificationServer] Closing Notification:",
// notification.appName, notification.id, notification.summary, notification.body
// )
// notification.dismiss()
// return
// }
notification.tracked = true
console.log(
"[NotificationServer] Notification received:",
notification.appName, notification.id, notification.summary, notification.body
)
notificationReceived(notification)
let timer = notificationTimerComponent.createObject(
notificationServerState,
{ notification: notification }
)
Hyprland.refreshMonitors()
waitTimer.start()
timer.start()
}
}
Component {
id: notificationTimerComponent
Timer {
id: waitTimer
required property var notification
interval: 2000
repeat: false
onTriggered: focusedMonitor = Hyprland.focusedMonitor
onTriggered: {
focusedMonitor = Hyprland.focusedMonitor
notificationReceived(notification)
if(!notification.lastGeneration){
notificationSound.play()
}
// destroy()
}
}
}
Component.onCompleted: {
focusedMonitor = Hyprland.focusedMonitor
console.log("[NotificationServer]: Component completed")
}
}