feat: add notification sound and notification window enhancements. Ready for alpha.NotificationBody needs work
This commit is contained in:
@@ -3,3 +3,4 @@ Colors.qml
|
|||||||
modules/wlogout/scripts/helper.sh
|
modules/wlogout/scripts/helper.sh
|
||||||
config/path/ConfigPath.qml
|
config/path/ConfigPath.qml
|
||||||
config/path/WallpaperPath.qml
|
config/path/WallpaperPath.qml
|
||||||
|
modules/notification/assets/notification.ogg
|
||||||
@@ -105,6 +105,15 @@ download \
|
|||||||
"$SCRIPT_DIR/modules/jl/assets/correct.svg" \
|
"$SCRIPT_DIR/modules/jl/assets/correct.svg" \
|
||||||
"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
|
# Nerd Font
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
|
|||||||
@@ -7,20 +7,20 @@ import Quickshell.Services.Pipewire
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
PwObjectTracker {
|
|
||||||
objects: [ Pipewire.defaultAudioSource ]
|
|
||||||
}
|
|
||||||
|
|
||||||
property var audioSource: 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 bool mute: audioSource?.audio?.muted ?? false
|
||||||
property real volume: Math.round((audioSource?.audio?.volume ?? 0) * 1000) / 10
|
property real volume: Math.round((audioSource?.audio?.volume ?? 0) * 1000) / 10
|
||||||
|
|
||||||
property bool volumeChanged: false
|
property bool volumeChanged: false
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: [ root.audioSource ]
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: audioSource?.audio
|
target: root.audioSource?.audio
|
||||||
|
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
root.volumeChanged = true;
|
root.volumeChanged = true;
|
||||||
|
|||||||
@@ -5,18 +5,24 @@ import "../../config"
|
|||||||
import "./notificationServer"
|
import "./notificationServer"
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
mask: Region {}
|
// mask: Region {}
|
||||||
|
|
||||||
id: notificationWindow
|
id: notificationWindow
|
||||||
|
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
anchors.left: true
|
anchors.left: true
|
||||||
|
|
||||||
implicitHeight: screen.height/6
|
// implicitHeight: screen.height/6
|
||||||
implicitWidth: screen.width/6
|
implicitHeight: contentHeight+(content.spacing*(notificationListModel.count-1))
|
||||||
|
implicitWidth: contentWidth
|
||||||
|
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: notificationListModel
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: NotificationS
|
target: NotificationS
|
||||||
|
|
||||||
@@ -25,17 +31,11 @@ PanelWindow {
|
|||||||
return
|
return
|
||||||
if (NotificationS.correctMonitor(screen)) {
|
if (NotificationS.correctMonitor(screen)) {
|
||||||
console.log("[Notification]: This notification belongs to me!", screen.x, screen.y)
|
console.log("[Notification]: This notification belongs to me!", screen.x, screen.y)
|
||||||
// currentNotifications.push(notification)
|
notificationListModel.append({notificationData: notification})
|
||||||
// currentNotifications = currentNotifications
|
|
||||||
currentNotification = notification
|
|
||||||
currentNotifications = currentNotifications.concat([notification])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var currentNotification: null
|
|
||||||
property var currentNotifications: []
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
@@ -44,22 +44,53 @@ PanelWindow {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
id: content
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 10
|
spacing: -2
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: currentNotifications
|
id: notificationRepeater
|
||||||
|
model: notificationListModel
|
||||||
|
|
||||||
delegate: NotificationBody {
|
delegate: NotificationBody {
|
||||||
required property int index
|
required property int index
|
||||||
|
required property var notificationData
|
||||||
|
|
||||||
notification: currentNotifications[index]
|
notification: notificationData
|
||||||
width: notificationWindow.width
|
|
||||||
|
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: {
|
Component.onCompleted: {
|
||||||
console.log(
|
console.log(
|
||||||
"[Notification]:",
|
"[Notification]:",
|
||||||
|
|||||||
@@ -6,7 +6,84 @@ Rectangle {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property var notification
|
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: {
|
Component.onCompleted: {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -16,54 +93,4 @@ Rectangle {
|
|||||||
notification.appName
|
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 QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import Quickshell.Services.Notifications
|
import Quickshell.Services.Notifications
|
||||||
|
import "../../../config"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: notificationServerState
|
id: notificationServerState
|
||||||
|
|
||||||
property var focusedMonitor: null
|
property var focusedMonitor: null
|
||||||
property var newestNotification: null
|
property var newestNotification: null
|
||||||
property var trackedNotifications: [] // TODO
|
|
||||||
|
|
||||||
signal notificationReceived(var notification)
|
signal notificationReceived(var notification)
|
||||||
|
|
||||||
@@ -18,33 +19,69 @@ Item {
|
|||||||
return (Hyprland.monitorFor(screen) === focusedMonitor)
|
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 {
|
NotificationServer {
|
||||||
id: notificationServer
|
id: notificationServer
|
||||||
onNotification: function(notification) {
|
onNotification: function(notification) {
|
||||||
if(notification.lastGeneration){
|
// if(notification.lastGeneration){
|
||||||
console.log(
|
// console.log(
|
||||||
"[NotificationServer] Closing Notification:",
|
// "[NotificationServer] Closing Notification:",
|
||||||
notification.appName, notification.id, notification.summary, notification.body
|
// notification.appName, notification.id, notification.summary, notification.body
|
||||||
)
|
// )
|
||||||
notification.dismiss()
|
// notification.dismiss()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
notification.tracked = true
|
notification.tracked = true
|
||||||
console.log(
|
console.log(
|
||||||
"[NotificationServer] Notification received:",
|
"[NotificationServer] Notification received:",
|
||||||
notification.appName, notification.id, notification.summary, notification.body
|
notification.appName, notification.id, notification.summary, notification.body
|
||||||
)
|
)
|
||||||
notificationReceived(notification)
|
|
||||||
|
let timer = notificationTimerComponent.createObject(
|
||||||
|
notificationServerState,
|
||||||
|
{ notification: notification }
|
||||||
|
)
|
||||||
|
|
||||||
Hyprland.refreshMonitors()
|
Hyprland.refreshMonitors()
|
||||||
waitTimer.start()
|
timer.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: notificationTimerComponent
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: waitTimer
|
required property var notification
|
||||||
|
|
||||||
interval: 2000
|
interval: 2000
|
||||||
repeat: false
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user