import QtQuick import Quickshell import Quickshell.Services.Notifications import Quickshell.Widgets import "../../config" MouseArea { id: indicator required property var manager height: bar.height width: indicatorRow.width + Config.spacing_fun(height) * 2 hoverEnabled: true cursorShape: Qt.PointingHandCursor property bool popupOpen: false onClicked: { popupOpen = !popupOpen if (popupOpen) { notifPopup.visible = true } else { notifPopup.visible = false } } Rectangle { anchors.fill: parent color: indicator.containsMouse || popupOpen ? Colors.tertiaryContainer : "transparent" radius: Config.radius_fun(indicator.height) border.width: (indicator.containsMouse || popupOpen) ? Config.border_width : 0 border.color: (indicator.containsMouse || popupOpen) ? Colors.tertiaryContainer_fg : "transparent" Row { id: indicatorRow anchors.centerIn: parent spacing: Config.spacing_fun(indicator.height) / 2 Item { id: iconWrap width: Config.iconSize(indicator.height) height: Config.iconSize(indicator.height) anchors.verticalCenter: parent.verticalCenter IconImage { id: notifIcon anchors.fill: parent icon: manager.lastNotification ? manager.lastNotification.appIcon : "" sourceSize.width: Config.iconSize(indicator.height) sourceSize.height: Config.iconSize(indicator.height) visible: manager.lastNotification && manager.lastNotification.appIcon !== "" } Text { anchors.centerIn: parent text: "\uF0F3" font.pixelSize: Config.iconSize(indicator.height) font.family: Config.font visible: !notifIcon.visible color: indicator.containsMouse ? Colors.tertiary : Colors.secondary } } Rectangle { id: countBadge width: Math.max(badgeText.width + 6, 16) height: 16 radius: 8 visible: manager.notificationCount > 0 anchors.verticalCenter: parent.verticalCenter color: manager.hasCritical ? Colors.error : Colors.primary Text { id: badgeText anchors.centerIn: parent text: manager.notificationCount > 99 ? "99+" : manager.notificationCount.toString() font.pixelSize: Config.font_size_tiny(indicator.height) font.family: Config.font font.bold: true color: manager.hasCritical ? Colors.error_fg : Colors.primary_fg } } Rectangle { id: criticalDot width: 8 height: 8 radius: 4 visible: manager.hasCritical anchors.verticalCenter: parent.verticalCenter color: Colors.error SequentialAnimation on opacity { loops: Animation.Infinite PropertyAnimation { from: 1.0; to: 0.4; duration: 800 } PropertyAnimation { from: 0.4; to: 1.0; duration: 800 } } } } } PopupWindow { id: notifPopup visible: indicator.popupOpen color: "transparent" anchor { item: indicator edges: Edges.Top | Edges.Left gravity: Edges.Bottom | Edges.Right adjustment: PopupAdjustment.FlipY | PopupAdjustment.SlideX onAnchoring: { anchor.rect.x = (indicator.width - notifPopup.width) / 2 anchor.rect.y = indicator.height + 4 } } implicitWidth: 280 implicitHeight: Math.max(60, popupContent.height + 24) Rectangle { anchors.fill: parent color: Qt.alpha(Colors.primaryContainer, 0.95) radius: Config.radius border { width: Config.border_width color: Colors.primaryContainer_fg } Column { id: popupContent width: parent.width anchors { top: parent.top left: parent.left right: parent.right margins: 12 } spacing: 8 Text { text: "Notifications" font.pixelSize: Config.font_size_small(indicator.height) font.family: Config.font font.bold: true color: Colors.primary anchors.horizontalCenter: parent.horizontalCenter visible: manager.notificationCount > 0 } Repeater { model: manager.notifications delegate: Rectangle { width: parent.width height: delegateText.height + 10 radius: Config.small_radius color: Qt.alpha(Colors.surfaceContainerHighest, 0.5) required property var model property var notif: model.object Row { anchors { left: parent.left right: parent.right margins: 6 verticalCenter: parent.verticalCenter } spacing: 6 Rectangle { width: 6 height: 24 radius: 3 color: { if (notif.urgency === NotificationUrgency.Critical) return Colors.error if (notif.urgency === NotificationUrgency.Low) return Colors.outline return Colors.primary } anchors.verticalCenter: parent.verticalCenter } Column { width: parent.width - 24 spacing: 2 Text { id: delegateText width: parent.width text: notif.summary || notif.appName || "Notification" font.pixelSize: Config.labelSize(indicator.height) font.family: Config.font font.bold: true color: Colors.surface_fg elide: Text.ElideRight maximumLineCount: 1 } Text { width: parent.width text: notif.body || "" visible: text !== "" font.pixelSize: Config.font_size_tiny(indicator.height) font.family: Config.font color: Colors.surfaceVariant_fg elide: Text.ElideRight maximumLineCount: 1 } } } } } Text { text: "No notifications" font.pixelSize: Config.font_size_small(indicator.height) font.family: Config.font color: Colors.secondary anchors.horizontalCenter: parent.horizontalCenter visible: manager.notificationCount === 0 height: 30 } } } onVisibleChanged: { if (!visible) indicator.popupOpen = false } } }