import Quickshell import QtQuick import Quickshell.Widgets import qs.Services import QtQuick.Controls import QtQuick.Layouts import QtQuick.Shapes Item { id: root width: icon.width + 8 height: parent.height anchors.verticalCenter: parent.verticalCenter WrapperMouseArea { id: widgetMouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: NotificationManager.displayInhibited = !NotificationManager.displayInhibited Rectangle { anchors.fill: parent color: { if (widgetMouseArea.containsMouse) { return "#33808080"; } return "transparent"; } Item { anchors.fill: parent MaterialDesignIcon { id: icon implicitSize: 14 anchors.centerIn: parent icon: NotificationManager.active ? "message" : "message-off" color: { if (!NotificationManager.active && !NotificationManager.displayInhibited) return "#f28a21"; if (NotificationManager.displayInhibited) return "white"; return "#555"; } } } } } Loader { id: tooltipLoader active: false Connections { target: widgetMouseArea function onContainsMouseChanged() { if (widgetMouseArea.containsMouse) tooltipLoader.active = true; } } sourceComponent: PopupWindow { id: tooltip property bool nextVisible: NotificationManager.active && (widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse) anchor { item: widgetMouseArea edges: Edges.Bottom | Edges.Left } visible: false onNextVisibleChanged: hangTimer.restart() Timer { id: hangTimer interval: tooltip.visible ? 100 : 500 onTriggered: { tooltip.visible = tooltip.nextVisible; if (!tooltip.visible) tooltipLoader.active = false; } } implicitWidth: 400 implicitHeight: Math.min(tooltip.screen.height * 0.66, Math.max(100, scroll.contentHeight + 16)) color: "black" WrapperMouseArea { id: tooltipMouseArea hoverEnabled: true enabled: true anchors.fill: parent WrapperItem { margin: 8 ScrollView { id: scroll contentWidth: availableWidth // ScrollBar.vertical.policy: ScrollBar.AlwaysOn ColumnLayout { id: historyLayout anchors { left: parent.left right: parent.right } spacing: 8 Repeater { model: ScriptModel { values: [...NotificationManager.history].reverse().map(o => o.notification) } delegate: GridLayout { id: notif Layout.fillWidth: true Layout.preferredHeight: notifSummary.contentHeight + (notifBody.visible ? notifBody.contentHeight + 8 : 0) + (notifSep.visible ? notifSep.height + 8 : 0) + notifTime.contentHeight + 8 required property var modelData required property int index columnSpacing: 8 rowSpacing: 8 columns: notifImage.visible ? 2 : 1 rows: { var res = 2; if (notifBody.visible) res += 1; if (notifSep.visible) res += 1; return res; } Shape { id: notifSep visible: notif.index != 0 height: 2 width: 400 - 32 ShapePath { strokeWidth: 2 strokeColor: "#20ffffff" startX: 0; startY: 0; PathLine { x: 400 - 32; y: 0; } } Layout.row: 0 Layout.column: 0 Layout.columnSpan: notifImage.visible ? 2 : 1 Layout.alignment: Qt.AlignHCenter } Text { id: notifSummary text: notif.modelData?.summary ?? "" font.pointSize: 10 font.family: "Fira Sans" font.italic: true color: "white" maximumLineCount: 1 elide: Text.ElideRight Layout.fillWidth: true Layout.row: notifSep.visible ? 1 : 0 Layout.column: notifImage.visible ? 1 : 0 } Image { id: notifImage visible: (notif.modelData?.image || notif.modelData?.appIcon) ?? false onStatusChanged: { if (notifImage.status == Image.Error) notifImage.visible = false; } source: (notif.modelData?.image || notif.modelData?.appIcon) ?? "" fillMode: Image.PreserveAspectFit asynchronous: true smooth: true mipmap: true Layout.maximumWidth: 50 Layout.column: 0 Layout.row: notifSep.visible ? 1 : 0 Layout.fillHeight: true Layout.rowSpan: notifBody.visible ? 3 : 2 } Text { id: notifBody visible: notif.modelData?.body ?? false text: notif.modelData?.body ?? "" textFormat: Text.RichText wrapMode: Text.Wrap font.pointSize: 10 font.family: "Fira Sans" color: "white" Layout.fillWidth: true Layout.row: notifSep.visible ? 2 : 1 Layout.column: notifImage.visible ? 1 : 0 } Text { id: notifTime Connections { target: NotificationManager.clock function onDateChanged() { notifTime.text = NotificationManager.formatTime(notif.modelData?.receivedTime); } } text: NotificationManager.formatTime(notif.modelData?.receivedTime) font.pointSize: 8 font.family: "Fira Sans" font.italic: true color: "#555" maximumLineCount: 1 horizontalAlignment: Text.AlignRight Layout.fillWidth: true Layout.row: { var res = 1; if (notifSep.visible) res += 1; if (notifBody.visible) res += 1; return res; } Layout.column: notifImage.visible ? 1 : 0 } } } } } } } } } }