import QtQuick import QtQuick.Layouts Item { id: root required property int nid required property string appName required property string summary required property string body required property string appIcon required property string image required property string desktopEntry required property int urgency required property var timestamp signal dismissed() implicitHeight: inner.implicitHeight + 16 height: implicitHeight Rectangle { id: bg anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 anchors.topMargin: 2 anchors.bottomMargin: 2 color: itemClick.containsMouse ? Theme.bg1 : "transparent" radius: Theme.radius Behavior on color { ColorAnimation { duration: 100 } } RowLayout { id: inner // Right margin reserves space so content does not overlap the close button anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 8 anchors.rightMargin: 32 spacing: 8 Rectangle { width: 34 height: 34 radius: 17 color: root.urgency === 2 ? Theme.redN : root.urgency === 0 ? Theme.bg3 : Theme.blueN Layout.alignment: Qt.AlignVCenter Image { id: iconImg anchors.fill: parent anchors.margins: 5 source: { if (root.image) return root.image if (!root.appIcon) return "" if (root.appIcon.startsWith("/")) return root.appIcon return Quickshell.iconPath(root.appIcon, true) || "" } fillMode: Image.PreserveAspectFit visible: status === Image.Ready && source !== "" smooth: true } Text { anchors.centerIn: parent visible: !iconImg.visible text: root.appName.charAt(0).toUpperCase() color: Theme.fg font.pixelSize: Theme.fontSize font.family: Theme.font font.bold: true } } ColumnLayout { Layout.fillWidth: true spacing: 2 RowLayout { Layout.fillWidth: true spacing: 6 Text { text: root.summary.length > 0 ? root.summary : root.appName color: Theme.text font.pixelSize: Theme.fontSize font.family: Theme.font font.bold: true elide: Text.ElideRight Layout.fillWidth: true } Text { text: NotificationsState.formatTime(root.timestamp) color: Theme.subtle font.pixelSize: Theme.fontSizeSmall font.family: Theme.font } } Text { visible: root.body.length > 0 text: root.body textFormat: Text.StyledText color: Theme.fg2 font.pixelSize: Theme.fontSizeSmall font.family: Theme.font wrapMode: Text.WordWrap maximumLineCount: 2 elide: Text.ElideRight Layout.fillWidth: true } Text { visible: root.summary.length > 0 text: root.appName color: Theme.muted font.pixelSize: Theme.fontSizeSmall - 1 font.family: Theme.font } } } Rectangle { id: closeArea width: 20 height: 20 radius: 10 anchors.top: parent.top anchors.right: parent.right anchors.topMargin: 5 anchors.rightMargin: 5 color: closeBtn.containsMouse ? Theme.bg3 : "transparent" Behavior on color { ColorAnimation { duration: 100 } } Text { anchors.centerIn: parent text: "✕" color: closeBtn.containsMouse ? Theme.fg : Theme.subtle font.pixelSize: 10 opacity: closeBtn.containsMouse ? 1.0 : 0.5 Behavior on color { ColorAnimation { duration: 100 } } Behavior on opacity { NumberAnimation { duration: 100 } } } MouseArea { id: closeBtn anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: root.dismissed() } } // Does not extend to the right edge to avoid overlapping the close button MouseArea { id: itemClick anchors.fill: parent anchors.rightMargin: 28 hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { NotificationsState.invokeDefault(root.nid, root.desktopEntry || root.appName) root.dismissed() } } } }