summaryrefslogtreecommitdiff
path: root/modules/notifications/NotificationToasts.qml
diff options
context:
space:
mode:
authorATMDA <atdma2600@gmail.com>2025-11-12 14:51:22 -0500
committerATMDA <atdma2600@gmail.com>2025-11-12 14:51:22 -0500
commitc1510b547645de5e8f70f6be99a0ba894b797241 (patch)
treece67826496f6530ef0ad09482f28ffc85c79eb51 /modules/notifications/NotificationToasts.qml
parenttray: if background enabled and empty, background is hidden (diff)
downloadcaelestia-shell-c1510b547645de5e8f70f6be99a0ba894b797241.tar.gz
caelestia-shell-c1510b547645de5e8f70f6be99a0ba894b797241.tar.bz2
caelestia-shell-c1510b547645de5e8f70f6be99a0ba894b797241.zip
notifs/toasts: reworked notifications and toasts and how they display and work together. see pull request comment.
Diffstat (limited to 'modules/notifications/NotificationToasts.qml')
-rw-r--r--modules/notifications/NotificationToasts.qml186
1 files changed, 186 insertions, 0 deletions
diff --git a/modules/notifications/NotificationToasts.qml b/modules/notifications/NotificationToasts.qml
new file mode 100644
index 0000000..96fe817
--- /dev/null
+++ b/modules/notifications/NotificationToasts.qml
@@ -0,0 +1,186 @@
+pragma ComponentBehavior: Bound
+
+import qs.components
+import qs.config
+import qs.services
+import Quickshell
+import Quickshell.Widgets
+import QtQuick
+
+Item {
+ id: root
+
+ required property Item panels
+
+ readonly property int spacing: Appearance.spacing.small
+ readonly property int maxToasts: 5
+ readonly property bool listVisible: panels.notifications.content.shouldShow
+
+ property bool flag
+ property var activeToasts: new Set()
+
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.margins: Appearance.padding.normal
+
+ implicitWidth: Config.notifs.sizes.width
+ implicitHeight: {
+ if (listVisible)
+ return 0;
+
+ let height = -spacing;
+ for (let i = 0; i < repeater.count; i++) {
+ const item = repeater.itemAt(i) as ToastWrapper;
+ if (item && !item.modelData.closed && !item.previewHidden)
+ height += item.implicitHeight + spacing;
+ }
+ return height;
+ }
+
+ opacity: listVisible ? 0 : 1
+ visible: opacity > 0
+
+ Behavior on opacity {
+ Anim {
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ }
+ }
+
+ Repeater {
+ id: repeater
+
+ model: ScriptModel {
+ values: {
+ const toasts = [];
+ let visibleCount = 0;
+
+ for (const notif of Notifs.list) {
+ if (notif.showAsToast) {
+ root.activeToasts.add(notif);
+ }
+ if (notif.closed) {
+ root.activeToasts.delete(notif);
+ }
+ }
+
+ for (const notif of Notifs.list) {
+ if (root.activeToasts.has(notif)) {
+ toasts.push(notif);
+ if (notif.showAsToast && !notif.closed) {
+ visibleCount++;
+ if (visibleCount > root.maxToasts)
+ break;
+ }
+ }
+ }
+ return toasts;
+ }
+ onValuesChanged: root.flagChanged()
+ }
+
+ ToastWrapper {}
+ }
+
+ component ToastWrapper: MouseArea {
+ id: toast
+
+ required property int index
+ required property Notifs.Notif modelData
+
+ readonly property bool previewHidden: {
+ let extraHidden = 0;
+ for (let i = 0; i < index; i++) {
+ const item = repeater.itemAt(i);
+ if (item && item.modelData.closed)
+ extraHidden++;
+ }
+ return index >= root.maxToasts + extraHidden;
+ }
+
+ opacity: modelData.closed || previewHidden || !modelData.showAsToast ? 0 : 1
+ scale: modelData.closed || previewHidden || !modelData.showAsToast ? 0.7 : 1
+
+ anchors.topMargin: {
+ root.flag;
+ let margin = 0;
+ for (let i = 0; i < index; i++) {
+ const item = repeater.itemAt(i) as ToastWrapper;
+ if (item && !item.modelData.closed && !item.previewHidden)
+ margin += item.implicitHeight + root.spacing;
+ }
+ return margin;
+ }
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ implicitHeight: toastInner.implicitHeight
+
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
+ onClicked: {
+ modelData.showAsToast = false;
+ modelData.close();
+ }
+
+ Component.onCompleted: modelData.lock(this)
+
+ onPreviewHiddenChanged: {
+ if (initAnim.running && previewHidden)
+ initAnim.stop();
+ }
+
+ Anim {
+ id: initAnim
+
+ Component.onCompleted: running = !toast.previewHidden
+
+ target: toast
+ properties: "opacity,scale"
+ from: 0
+ to: 1
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+
+ ParallelAnimation {
+ running: toast.modelData.closed || (!toast.modelData.showAsToast && !toast.modelData.closed)
+ onStarted: toast.anchors.topMargin = toast.anchors.topMargin
+ onFinished: {
+ if (toast.modelData.closed)
+ toast.modelData.unlock(toast);
+ }
+
+ Anim {
+ target: toast
+ property: "opacity"
+ to: 0
+ }
+ Anim {
+ target: toast
+ property: "scale"
+ to: 0.7
+ }
+ }
+
+ NotificationToast {
+ id: toastInner
+
+ modelData: toast.modelData
+ }
+
+ Behavior on opacity {
+ Anim {}
+ }
+
+ Behavior on scale {
+ Anim {}
+ }
+
+ Behavior on anchors.topMargin {
+ Anim {
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ }
+ }
+}