summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml')
-rw-r--r--accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml332
1 files changed, 332 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml
new file mode 100644
index 00000000..03406687
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml
@@ -0,0 +1,332 @@
1import QtQml
2import QtQml.Models
3import QtQuick
4import Quickshell
5import Quickshell.Widgets
6import Quickshell.Wayland
7import qs.Services
8import QtQuick.Layouts
9import Quickshell.Services.Notifications
10
11Scope {
12 readonly property ShellScreen activeScreen: Array.from(Quickshell.screens).find(screen => screen.name === Array.from(NiriService.workspaces).find(ws => ws.is_focused)?.output) ?? null
13
14 Instantiator {
15 id: notifsRepeater
16
17 model: ScriptModel {
18 values: NotificationManager.active ? [] : NotificationManager.groups
19 }
20
21 delegate: PanelWindow {
22 id: notifWindow
23
24 required property var modelData
25 required property var index
26
27 property int activeIx: modelData.length - 1
28 onModelDataChanged: {
29 notifWindow.activeIx = modelData.length - 1;
30 }
31
32 property color textColor: {
33 if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Low)
34 return "#ff999999";
35 return "white";
36 }
37 property color backgroundColor: {
38 if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Critical)
39 return "#dd900000";
40 return "black";
41 }
42
43 anchors {
44 right: true
45 top: true
46 }
47
48 readonly property real spaceAbove: {
49 var res = 0;
50 for (let i = 0; i < notifWindow.index; i++) {
51 (_ => {})(notifsRepeater.objectAt(i).modelData);
52 res += notifsRepeater.objectAt(i).height + 8;
53 }
54 return res;
55 }
56
57 margins {
58 right: 26 + 8
59 top: 8 + spaceAbove
60 }
61
62 color: "transparent"
63
64 implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + (notifTime.visible ? 8 + notifTime.contentHeight : 0) + 16
65 implicitWidth: 400
66
67 WrapperMouseArea {
68 enabled: true
69
70 anchors.fill: parent
71
72 cursorShape: Qt.PointingHandCursor
73
74 onClicked: {
75 for (const notif of notifWindow.modelData)
76 notif.dismiss();
77 }
78
79 property real angleRem: 0
80 property real sensitivity: 1 / 120
81 onWheel: event => {
82 angleRem += event.angleDelta.y;
83 const d = Math.round(angleRem * sensitivity);
84 angleRem -= d / sensitivity;
85 notifWindow.activeIx = ((notifWindow.modelData?.length ?? 1) + notifWindow.activeIx - d) % (notifWindow.modelData?.length ?? 1);
86 }
87
88 Rectangle {
89 color: notifWindow.backgroundColor
90 anchors.fill: parent
91 border {
92 color: Qt.hsla(195/360, 1, 0.45, 1)
93 width: 2
94 }
95
96 GridLayout {
97 id: notifLayout
98
99 width: 400 - 16
100 anchors.fill: parent
101 anchors.margins: 8
102 columnSpacing: 8
103 rowSpacing: 8
104
105 columns: notifImage.visible ? 3 : 2
106 rows: {
107 var res = 1;
108 if (notifBody.visible)
109 res += 1;
110 if (notifActions.visible)
111 res += 1;
112 if (notifTime.visible)
113 res += 1;
114 return res;
115 }
116
117 Text {
118 id: notifCount
119
120 visible: notifWindow.modelData?.length > 1 ?? false
121 text: `${notifWindow.activeIx + 1}/${notifWindow.modelData?.length ?? ""}`
122
123 font.pointSize: 10
124 font.family: "Fira Sans"
125 font.bold: true
126 font.features: { "tnum": 1 }
127 color: notifWindow.textColor
128 maximumLineCount: 1
129
130 Layout.fillWidth: false
131 Layout.row: 0
132 Layout.column: notifImage.visible ? 1 : 0
133 }
134
135 Text {
136 id: notifSummary
137
138 text: notifWindow.modelData?.[notifWindow.activeIx]?.summary ?? ""
139
140 font.pointSize: 10
141 font.family: "Fira Sans"
142 font.italic: true
143 color: notifWindow.textColor
144 maximumLineCount: 1
145 elide: Text.ElideRight
146
147 Layout.fillWidth: true
148 Layout.row: 0
149 Layout.column: (notifCount.visible ? 1 : 0) + (notifImage.visible ? 1 : 0)
150 Layout.columnSpan: notifCount.visible ? 1 : 2
151 }
152
153 Image {
154 id: notifImage
155
156 visible: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? false
157
158 onStatusChanged: {
159 if (notifImage.status == Image.Error)
160 notifImage.visible = false;
161 }
162
163 source: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? ""
164 fillMode: Image.PreserveAspectFit
165 asynchronous: true
166 smooth: true
167 mipmap: true
168
169 Layout.maximumWidth: 50
170 Layout.column: 0
171 Layout.row: 0
172 Layout.fillHeight: true
173 Layout.rowSpan: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0)
174 }
175
176 Text {
177 id: notifBody
178
179 visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false
180 text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? ""
181 textFormat: Text.RichText
182 wrapMode: Text.Wrap
183
184 font.pointSize: 10
185 font.family: "Fira Sans"
186 color: notifWindow.textColor
187
188 Layout.fillWidth: true
189 Layout.row: 1
190 Layout.column: notifImage.visible ? 1 : 0
191 Layout.columnSpan: notifCount.visible ? 2 : 1
192 }
193
194 Text {
195 id: notifTime
196
197 Connections {
198 target: NotificationManager.clock
199 function onDateChanged() {
200 notifTime.text = NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime);
201 }
202 }
203
204 visible: notifTime.text && notifTime.text !== "now"
205 text: NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime)
206
207 font.pointSize: 8
208 font.family: "Fira Sans"
209 font.italic: true
210 color: "#555"
211 maximumLineCount: 1
212 horizontalAlignment: Text.AlignRight
213
214 Layout.fillWidth: true
215 Layout.row: notifBody.visible ? 2 : 1
216 Layout.column: notifImage.visible ? 1 : 0
217 Layout.columnSpan: 2
218 }
219
220 RowLayout {
221 id: notifActions
222
223 visible: notifWindow.modelData?.[notifWindow.activeIx]?.actions.length > 0 ?? false
224
225 spacing: 8
226 uniformCellSizes: true
227
228 width: 400 - 16
229 Layout.row: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0)
230 Layout.column: 0
231 Layout.columnSpan: 2 + (notifImage.visible ? 1 : 0)
232
233 Repeater {
234 model: ScriptModel {
235 values: notifWindow.modelData?.[notifWindow.activeIx]?.actions
236 }
237
238 delegate: WrapperMouseArea {
239 id: actionMouseArea
240
241 required property var modelData
242
243 height: actionLabelWrapper.implicitHeight
244 Layout.fillWidth: true
245 Layout.horizontalStretchFactor: 1
246
247 hoverEnabled: true
248 cursorShape: Qt.PointingHandCursor
249
250 onClicked: actionMouseArea.modelData?.invoke()
251
252 Rectangle {
253 anchors.fill: parent
254
255 color: actionMouseArea.containsMouse ? "#20ffffff" : "transparent"
256
257 border {
258 width: 2
259 color: "#20ffffff"
260 }
261
262 WrapperItem {
263 id: actionLabelWrapper
264
265 margin: 8
266 anchors.centerIn: parent
267
268 RowLayout {
269 id: actionLabelLayout
270
271 spacing: 8
272
273 IconImage {
274 id: actionIcon
275
276 visible: notifWindow.modelData?.[notifWindow.activeIx]?.hasActionIcons
277
278 onStatusChanged: {
279 if (actionIcon.status == Image.Error)
280 actionIcon.visible = false;
281 }
282
283 implicitSize: 16
284 source: {
285 if (!actionIcon.visible)
286 return "";
287
288 let icon = actionMouseArea.modelData?.identifier ?? ""
289 if (icon.includes("?path=")) {
290 const split = icon.split("?path=")
291 if (split.length !== 2)
292 return icon
293 const name = split[0]
294 const path = split[1]
295 const fileName = name.substring(
296 name.lastIndexOf("/") + 1)
297 return `file://${path}/${fileName}`
298 }
299 return icon
300 }
301 asynchronous: true
302 smooth: true
303 mipmap: true
304
305 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
306 }
307
308 Text {
309 id: actionLabel
310
311 visible: actionMouseArea.modelData?.text ?? false
312
313 text: actionMouseArea.modelData?.text ?? ""
314
315 font.pointSize: 10
316 font.family: "Fira Sans"
317 color: notifWindow.textColor
318 maximumLineCount: 1
319 elide: Text.ElideRight
320 }
321 }
322 }
323 }
324 }
325 }
326 }
327 }
328 }
329 }
330 }
331 }
332}