quickshell: add notification toast popup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
rope 2026-05-26 20:01:54 +01:00
parent 2ec4f400c6
commit 18ccb266c3

View file

@ -467,6 +467,7 @@ in
keepOnReload: true
onNotification: (notification) => {
notification.tracked = true;
notifToast.show(notification);
}
}
@ -1659,6 +1660,160 @@ in
}
}
}
// Notification toast popup
PopupWindow {
id: notifToast
property var currentNotif: null
property bool open: false
function show(notification) {
currentNotif = notification;
visible = true;
open = true;
_toastTimer.restart();
}
function dismiss() {
open = false;
_toastCloseDelay.start();
}
anchor.window: bar
anchor.rect.x: bar.width / 2 - 160
anchor.rect.y: bar.height
anchor.edges: Edges.Top | Edges.Left
anchor.gravity: Edges.Bottom | Edges.Right
visible: false
width: 320
height: toastContent.height + 2
color: "transparent"
Timer {
id: _toastTimer
interval: 5000
onTriggered: notifToast.dismiss()
}
Timer {
id: _toastCloseDelay
interval: 230
onTriggered: { notifToast.visible = false; notifToast.open = false; }
}
HoverHandler {
onHoveredChanged: {
if (hovered) _toastTimer.stop();
else _toastTimer.restart();
}
}
Rectangle {
id: toastContent
anchors.top: parent.top
anchors.topMargin: 2
anchors.horizontalCenter: parent.horizontalCenter
width: 316
height: toastCol.height + 16
radius: 8
color: "#E6${c.base00}"
clip: true
transform: Translate {
y: notifToast.open ? 0 : -(toastContent.height + 10)
Behavior on y {
NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
}
}
opacity: notifToast.open ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
}
Column {
id: toastCol
anchors.left: parent.left
anchors.right: toastDismiss.left
anchors.top: parent.top
anchors.margins: 8
spacing: 2
Text {
width: parent.width
text: notifToast.currentNotif ? (notifToast.currentNotif.summary || notifToast.currentNotif.appName) : ""
color: "#${c.base05}"
font.family: "FiraMono Nerd Font"
font.pixelSize: 12
font.weight: Font.Medium
elide: Text.ElideRight
}
Text {
width: parent.width
text: notifToast.currentNotif ? (notifToast.currentNotif.body || "") : ""
color: "#${c.base04}"
font.family: "FiraMono Nerd Font"
font.pixelSize: 11
elide: Text.ElideRight
maximumLineCount: 3
wrapMode: Text.Wrap
visible: text !== ""
}
// Action buttons
Row {
spacing: 4
visible: notifToast.currentNotif && notifToast.currentNotif.actions.length > 0
Repeater {
model: notifToast.currentNotif ? notifToast.currentNotif.actions : []
Rectangle {
required property var modelData
width: toastActionText.width + 12
height: toastActionText.height + 6
radius: 4
color: toastActionMa.containsMouse ? "#${c.base02}" : "#${c.base01}"
border.width: 1
border.color: "#${c.base02}"
Text {
id: toastActionText
anchors.centerIn: parent
text: modelData.text
color: "#${c.base05}"
font.family: "FiraMono Nerd Font"
font.pixelSize: 10
}
MouseArea {
id: toastActionMa
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: { modelData.invoke(); notifToast.dismiss(); }
}
}
}
}
}
// Dismiss X
Text {
id: toastDismiss
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 8
text: "\u{f0156}"
color: toastDismissMa.containsMouse ? "#${c.base05}" : "#${c.base03}"
font.family: "FiraMono Nerd Font"
font.pixelSize: 13
MouseArea {
id: toastDismissMa
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: { notifToast.currentNotif.dismiss(); notifToast.dismiss(); }
}
}
}
}
}
}
'';