mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-02 10:37:50 +00:00
Merge pull request #344 from FUFSoB/notifications-refine
Notifications improvements
This commit is contained in:
@@ -127,7 +127,9 @@
|
||||
"doNotDisturb": false,
|
||||
"monitors": [],
|
||||
"location": "top_right",
|
||||
"alwaysOnTop": false,
|
||||
"lastSeenTs": 0,
|
||||
"respectExpireTimeout": false,
|
||||
"lowUrgencyDuration": 3,
|
||||
"normalUrgencyDuration": 8,
|
||||
"criticalUrgencyDuration": 15,
|
||||
@@ -182,4 +184,4 @@
|
||||
"wallpaperChange": "",
|
||||
"darkModeChange": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,9 @@ Singleton {
|
||||
property bool doNotDisturb: false
|
||||
property list<string> monitors: []
|
||||
property string location: "top_right"
|
||||
property bool alwaysOnTop: false
|
||||
property real lastSeenTs: 0
|
||||
property bool respectExpireTimeout: false
|
||||
property int lowUrgencyDuration: 3
|
||||
property int normalUrgencyDuration: 8
|
||||
property int criticalUrgencyDuration: 15
|
||||
|
||||
@@ -37,6 +37,10 @@ Variants {
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
screen: modelData
|
||||
|
||||
WlrLayershell.namespace: "noctalia-notifications"
|
||||
WlrLayershell.layer: (Settings.isLoaded && Settings.data && Settings.data.notifications && Settings.data.notifications.alwaysOnTop) ? WlrLayer.Overlay : WlrLayer.Top
|
||||
|
||||
color: Color.transparent
|
||||
|
||||
readonly property string location: (Settings.isLoaded && Settings.data && Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
|
||||
@@ -103,7 +107,7 @@ Variants {
|
||||
|
||||
// Connect to animation signal from service - UPDATED TO USE ID
|
||||
Component.onCompleted: {
|
||||
NotificationService.animateAndRemove.connect(function (notificationId, index) {
|
||||
NotificationService.animateAndRemove.connect(function (notificationId) {
|
||||
// Find the delegate by notification ID
|
||||
var delegate = null
|
||||
if (notificationStack && notificationStack.children && notificationStack.children.length > 0) {
|
||||
@@ -116,11 +120,6 @@ Variants {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to index if ID lookup failed
|
||||
if (!delegate && notificationStack && notificationStack.children && notificationStack.children[index]) {
|
||||
delegate = notificationStack.children[index]
|
||||
}
|
||||
|
||||
if (delegate && delegate.animateOut) {
|
||||
delegate.animateOut()
|
||||
} else {
|
||||
@@ -159,6 +158,32 @@ Variants {
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
color: Color.mSurface
|
||||
|
||||
Rectangle {
|
||||
id: progressBar
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 2 * scaling
|
||||
color: "transparent"
|
||||
|
||||
property real availableWidth: parent.width - (2 * parent.radius)
|
||||
|
||||
Rectangle {
|
||||
x: parent.parent.radius + (parent.availableWidth * (1 - model.progress)) / 2
|
||||
width: parent.availableWidth * model.progress
|
||||
height: parent.height
|
||||
color: {
|
||||
if (model.urgency === NotificationUrgency.Critical || model.urgency === 2)
|
||||
return Color.mError
|
||||
else if (model.urgency === NotificationUrgency.Low || model.urgency === 0)
|
||||
return Color.mOnSurface
|
||||
else
|
||||
return Color.mPrimary
|
||||
}
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
||||
|
||||
// Animation properties
|
||||
property real scaleValue: 0.8
|
||||
property real opacityValue: 0.0
|
||||
|
||||
@@ -78,6 +78,13 @@ ColumnLayout {
|
||||
currentKey: Settings.data.notifications.location || "top_right"
|
||||
onSelected: key => Settings.data.notifications.location = key
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: "Always on top"
|
||||
description: "Display notifications above fullscreen windows and other layers."
|
||||
checked: Settings.data.notifications.alwaysOnTop
|
||||
onToggled: checked => Settings.data.notifications.alwaysOnTop = checked
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
@@ -96,6 +103,14 @@ ColumnLayout {
|
||||
description: "Configure how long notifications stay visible based on their urgency level."
|
||||
}
|
||||
|
||||
// Respect Expire Timeout (eg. --expire-time flag in notify-send)
|
||||
NToggle {
|
||||
label: "Respect expire timeout"
|
||||
description: "Use the expire timeout set in the notification."
|
||||
checked: Settings.data.notifications.respectExpireTimeout
|
||||
onToggled: checked => Settings.data.notifications.respectExpireTimeout = checked
|
||||
}
|
||||
|
||||
// Low Urgency Duration
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXXS * scaling
|
||||
|
||||
@@ -24,6 +24,7 @@ Singleton {
|
||||
// Internal state
|
||||
property var activeMap: ({})
|
||||
property var imageQueue: []
|
||||
property var progressTimers: ({})
|
||||
|
||||
// Simple image cacher
|
||||
PanelWindow {
|
||||
@@ -117,8 +118,10 @@ Singleton {
|
||||
"summary": (n.summary || ""),
|
||||
"body": stripTags(n.body || ""),
|
||||
"appName": getAppName(n.appName),
|
||||
"urgency": n.urgency || 1,
|
||||
"urgency": n.urgency < 0 || n.urgency > 2 ? 1 : n.urgency,
|
||||
"expireTimeout": n.expireTimeout,
|
||||
"timestamp": time,
|
||||
"progress": 1.0,
|
||||
"originalImage": image,
|
||||
"cachedImage": imageId ? (Settings.cacheDirImagesNotifications + imageId + ".png") : image,
|
||||
"actionsJson": JSON.stringify((n.actions || []).map(a => ({
|
||||
@@ -160,7 +163,6 @@ Singleton {
|
||||
function updateModel(model, id, prop, value) {
|
||||
for (var i = 0; i < model.count; i++) {
|
||||
if (model.get(i).id === id) {
|
||||
model.setProperty(i, prop, "")
|
||||
model.setProperty(i, prop, value)
|
||||
break
|
||||
}
|
||||
@@ -172,6 +174,7 @@ Singleton {
|
||||
if (activeList.get(i).id === id) {
|
||||
activeList.remove(i)
|
||||
delete activeMap[id]
|
||||
delete progressTimers[id]
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -179,19 +182,31 @@ Singleton {
|
||||
|
||||
// Auto-hide timer
|
||||
Timer {
|
||||
interval: 1000
|
||||
interval: 10
|
||||
repeat: true
|
||||
running: activeList.count > 0
|
||||
onTriggered: {
|
||||
const now = Date.now()
|
||||
const durations = [3000, 8000, 15000] // low, normal, critical
|
||||
const durations = [Settings.data.notifications?.lowUrgencyDuration * 1000 || 3000,
|
||||
Settings.data.notifications?.normalUrgencyDuration * 1000 || 8000,
|
||||
Settings.data.notifications?.criticalUrgencyDuration * 1000 || 15000]
|
||||
|
||||
for (var i = activeList.count - 1; i >= 0; i--) {
|
||||
const notif = activeList.get(i)
|
||||
const elapsed = now - notif.timestamp.getTime()
|
||||
var expire = 0
|
||||
|
||||
if (elapsed >= durations[notif.urgency] || elapsed >= 8000) {
|
||||
animateAndRemove(notif.id, i)
|
||||
if (Settings.data.notifications?.respectExpireTimeout)
|
||||
expire = notif.expireTimeout > 0 ? notif.expireTimeout : durations[notif.urgency]
|
||||
else
|
||||
expire = durations[notif.urgency]
|
||||
|
||||
const progress = Math.max(1.0 - (elapsed / expire), 0.0)
|
||||
updateModel(activeList, notif.id, "progress", progress)
|
||||
|
||||
if (elapsed >= expire) {
|
||||
animateAndRemove(notif.id)
|
||||
delete progressTimers[notif.id]
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -273,21 +288,22 @@ Singleton {
|
||||
}
|
||||
|
||||
historyList.append({
|
||||
"id": item.id || "",
|
||||
"summary": item.summary || "",
|
||||
"body": item.body || "",
|
||||
"appName": item.appName || "",
|
||||
"urgency": item.urgency || 1,
|
||||
"timestamp": time,
|
||||
"originalImage": item.originalImage || "",
|
||||
"cachedImage": cachedImage
|
||||
})
|
||||
"id": item.id || "",
|
||||
"summary": item.summary || "",
|
||||
"body": item.body || "",
|
||||
"appName": item.appName || "",
|
||||
"urgency": item.urgency < 0 || item.urgency > 2 ? 1 : item.urgency,
|
||||
"timestamp": time,
|
||||
"originalImage": item.originalImage || "",
|
||||
"cachedImage": cachedImage
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.error("Notifications", "Load failed:", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helpers
|
||||
function getAppName(name) {
|
||||
if (!name?.includes("."))
|
||||
@@ -380,7 +396,7 @@ Singleton {
|
||||
}
|
||||
|
||||
// Signals & connections
|
||||
signal animateAndRemove(string notificationId, int index)
|
||||
signal animateAndRemove(string notificationId)
|
||||
|
||||
Connections {
|
||||
target: Settings.data.notifications
|
||||
|
||||
Reference in New Issue
Block a user