diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-15 14:19:59 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-15 14:19:59 +0200 |
| commit | 9efe9458afdffe474944a04bc2f81864c0eac879 (patch) | |
| tree | 82a69b003a689837203feea661a0251f586530e0 /notifications/NotificationBalloon.qml | |
| parent | ba26690ea6ba23a3898dde10af349cf8608f5f25 (diff) | |
| download | quickshell-9efe9458afdffe474944a04bc2f81864c0eac879.tar.gz quickshell-9efe9458afdffe474944a04bc2f81864c0eac879.zip | |
feat(notifications): add the balloon shell
Reads the daemon's queue through the Notify singleton and draws a balloon
per live notification, bottom-right of DP-1 over conky. Suppression is here,
not in the daemon: dnd withholds low and normal, snooze withholds all, and
the drawer still lists them.
Diffstat (limited to 'notifications/NotificationBalloon.qml')
| -rw-r--r-- | notifications/NotificationBalloon.qml | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/notifications/NotificationBalloon.qml b/notifications/NotificationBalloon.qml new file mode 100644 index 0000000..dc8072f --- /dev/null +++ b/notifications/NotificationBalloon.qml @@ -0,0 +1,116 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import QtQuick + +// One balloon: icon at the left, app, summary and body, an X, and the click +// targets. The body is markup, which is why the daemon advertises body-markup. +Rectangle { + id: b + + required property var notification + + width: parent ? parent.width : 340 + implicitHeight: texts.implicitHeight + 20 + radius: 10 + color: Qt.alpha(Theme.base, 0.82) + border.width: 1 + border.color: Qt.alpha(Theme.text, 0.12) + + // The background click target is declared first so the X, declared later, + // sits above it and wins its corner. + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + cursorShape: Qt.PointingHandCursor + onClicked: mouse => { + if (mouse.button === Qt.RightButton) { + Notify.closeAll(); + return; + } + const acts = b.notification.actions || []; + const inert = b.notification.expires !== 0 && Date.now() >= b.notification.expires; + if (acts.length > 0 && !inert) Notify.actions(b.notification.id, acts); + else Notify.close(b.notification.id); + } + } + + Image { + id: icon + visible: b.notification.icon !== "" && b.notification.icon !== undefined + anchors { left: parent.left; top: parent.top; margins: 10 } + width: 32 + height: 32 + source: visible ? "file://" + b.notification.icon : "" + sourceSize { width: 64; height: 64 } + } + + Text { + id: close + anchors { right: parent.right; top: parent.top; margins: 6 } + width: 20 + height: 20 + text: "\uf00d" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font { family: Theme.iconFamily; pixelSize: 11 } + color: closeArea.containsMouse ? Theme.red : Theme.subtext + + MouseArea { + id: closeArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: Notify.close(b.notification.id) + } + } + + Column { + id: texts + anchors { + left: icon.visible ? icon.right : parent.left + leftMargin: 10 + right: parent.right + rightMargin: 10 + top: parent.top + topMargin: 10 + } + spacing: 2 + + Text { + width: parent.width + text: b.notification.app || "" + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4; bold: true } + color: Theme.subtext + } + + Text { + width: parent.width + text: b.notification.summary || "" + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.text + } + + Text { + width: parent.width + visible: text !== "" + text: b.notification.body || "" + textFormat: Text.RichText + wrapMode: Text.WordWrap + maximumLineCount: 3 + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.subtext + } + } +} |
