diff options
| author | ATMDA <atdma2600@gmail.com> | 2025-11-12 14:51:22 -0500 |
|---|---|---|
| committer | ATMDA <atdma2600@gmail.com> | 2025-11-12 14:51:22 -0500 |
| commit | c1510b547645de5e8f70f6be99a0ba894b797241 (patch) | |
| tree | ce67826496f6530ef0ad09482f28ffc85c79eb51 /modules/notifications/NotificationToasts.qml | |
| parent | tray: if background enabled and empty, background is hidden (diff) | |
| download | caelestia-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.qml | 186 |
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 + } + } + } +} |