quickshell: fix notification toast as separate Variants popup

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

View file

@ -458,6 +458,10 @@ in
import Qt5Compat.GraphicalEffects
ShellRoot {
id: root
property var latestNotification: null
signal notificationReceived()
NotificationServer {
id: notifServer
bodySupported: true
@ -467,7 +471,8 @@ in
keepOnReload: true
onNotification: (notification) => {
notification.tracked = true;
notifToast.show(notification);
root.latestNotification = notification;
root.notificationReceived();
}
}
@ -1660,157 +1665,171 @@ 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();
// Notification toast popup
Variants {
model: Quickshell.screens
PopupWindow {
id: notifToast
required property var modelData
screen: modelData
property var currentNotif: null
property bool open: false
Connections {
target: root
function onNotificationReceived() {
if (notifToast.modelData === Quickshell.screens[0]) {
notifToast.show(root.latestNotification);
}
}
}
function dismiss() {
open = false;
_toastCloseDelay.start();
function show(notification) {
currentNotif = notification;
visible = true;
open = true;
_toastTimer.restart();
}
function dismiss() {
open = false;
_toastCloseDelay.start();
}
anchor.rect.x: (screen ? screen.width / 2 : 0) - 160
anchor.rect.y: 30
anchor.edges: Edges.Top | Edges.Left
visible: false
implicitWidth: 320
implicitHeight: 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();
}
}
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"
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
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();
transform: Translate {
y: notifToast.open ? 0 : -(toastContent.height + 10)
Behavior on y {
NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
}
}
Rectangle {
id: toastContent
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.topMargin: 2
anchors.horizontalCenter: parent.horizontalCenter
width: 316
height: toastCol.height + 16
radius: 8
color: "#E6${c.base00}"
clip: true
anchors.margins: 8
spacing: 2
transform: Translate {
y: notifToast.open ? 0 : -(toastContent.height + 10)
Behavior on y {
NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
}
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
}
opacity: notifToast.open ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
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 !== ""
}
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(); }
}
// 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(); }
}
// 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(); }
}
}
}