Files
tshell/modules/osd/NotificationCard.qml
2026-08-24 15:45:02 +05:30

117 lines
3.5 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Notifications
import qs.config
Rectangle {
id: root
property Notification notif
readonly property bool critical: notif?.urgency === NotificationUrgency.Critical
readonly property int timeoutDuration: {
const seconds = Math.round((notif?.expireTimeout ?? 0) / 1000);
return seconds > 0 ? seconds : 5;
}
readonly property string iconName: {
if (!notif)
return "";
const hints = notif.hints ?? {};
const hinted = String(hints["image-path"] ?? hints["image_path"] ?? hints["icon_path"] ?? "");
return notif.appIcon || notif.desktopEntry || hinted;
}
readonly property url themedIconUrl: iconName !== "" ? Quickshell.iconPath(iconName, false) : ""
readonly property url iconUrl: `${Quickshell.shellPath("assets")}/icons/notification.svg`
implicitWidth: 360
implicitHeight: content.implicitHeight + Appearance.padding * 2
radius: Appearance.radius
color: Appearance.colors.background
border.width: critical ? 1 : 0
border.color: Appearance.colors.accent
Timer {
id: countdown
interval: root.timeoutDuration * 1000
repeat: false
running: !root.critical && !hovered.hovered && visible
onTriggered: root.notif.expire()
}
HoverHandler {
id: hovered
}
RowLayout {
id: content
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Appearance.padding
spacing: Appearance.spacing
Item {
width: 40
height: 40
Layout.alignment: Qt.AlignTop
Image {
anchors.fill: parent
source: root.iconUrl
fillMode: Image.PreserveAspectFit
smooth: true
}
Image {
anchors.fill: parent
source: root.themedIconUrl
visible: root.themedIconUrl !== "" && status === Image.Ready
fillMode: Image.PreserveAspectFit
smooth: true
}
}
ColumnLayout {
spacing: 2
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Text {
text: root.notif?.appName ?? ""
color: Appearance.colors.accent
font.family: Appearance.font.family
font.pointSize: Appearance.font.pointSize - 2
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: root.notif?.summary ?? ""
color: Appearance.colors.foreground
font.family: Appearance.font.family
font.pointSize: Appearance.font.pointSize
font.weight: Font.DemiBold
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: root.notif?.body ?? ""
visible: text !== ""
color: Appearance.colors.foreground
opacity: 0.8
font.family: Appearance.font.family
font.pointSize: Appearance.font.pointSize - 1
wrapMode: Text.Wrap
maximumLineCount: 3
elide: Text.ElideRight
textFormat: Text.PlainText
Layout.fillWidth: true
}
}
}
}