Kirigami2

PassiveNotificationsManager.qml
1/*
2 * SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7import QtQuick
8import QtQuick.Controls as QQC2
9import QtQuick.Layouts
10import org.kde.kirigami as Kirigami
11
12/**
13 * @brief PassiveNotificationManager is meant to display small, passive and inline notifications in the app.
14 *
15 * It is used to show messages of limited importance that make sense only when
16 * the user is using the application and wouldn't be suited as a global
17 * system-wide notification.
18*/
19Item {
20 id: root
21
22 readonly property int maximumNotificationWidth: {
23 if (Kirigami.Settings.isMobile) {
24 return applicationWindow().width - Kirigami.Units.largeSpacing * 4
25 } else {
26 return Math.min(Kirigami.Units.gridUnit * 25, applicationWindow().width / 1.5)
27 }
28 }
29
30 readonly property int maximumNotificationCount: 4
31
32 function showNotification(message, timeout, actionText, callBack) {
33 if (!message) {
34 return;
35 }
36
37 let interval = 7000;
38
39 if (timeout === "short") {
40 interval = 4000;
41 } else if (timeout === "long") {
42 interval = 12000;
43 } else if (timeout > 0) {
44 interval = timeout;
45 }
46
47 // this wrapper is necessary because of Qt casting a function into an object
48 const callBackWrapperObj = callBackWrapper.createObject(listView, { callBack })
49
50 // set empty string & function for qml not to complain
51 notificationsModel.append({
52 text: message,
53 actionButtonText: actionText || "",
54 closeInterval: interval,
55 callBackWrapper: callBackWrapperObj
56 })
57 // remove the oldest notification if new notification count would exceed 3
58 if (notificationsModel.count === maximumNotificationCount) {
59 if (listView.itemAtIndex(0).hovered === true) {
60 hideNotification(1)
61 } else {
62 hideNotification()
63 }
64 }
65 }
66
67 /**
68 * @brief Remove a notification at specific index. By default, index is set to 0.
69 */
70 function hideNotification(index = 0) {
71 if (index >= 0 && notificationsModel.count > index) {
72 const callBackWrapperObj = notificationsModel.get(index).callBackWrapper
73 if (callBackWrapperObj) {
74 callBackWrapperObj.destroy()
75 }
76 notificationsModel.remove(index)
77 }
78 }
79
80 // we have to set height to show more than one notification
81 height: Math.min(applicationWindow().height, Kirigami.Units.gridUnit * 10)
82
83 implicitHeight: listView.implicitHeight
84 implicitWidth: listView.implicitWidth
85
86 Kirigami.Theme.inherit: false
87 Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
88
89 ListModel {
90 id: notificationsModel
91 }
92
93 ListView {
94 id: listView
95
96 anchors.fill: parent
97 anchors.bottomMargin: Kirigami.Units.largeSpacing
98
99 implicitWidth: root.maximumNotificationWidth
100 spacing: Kirigami.Units.smallSpacing
101 model: notificationsModel
102 verticalLayoutDirection: ListView.BottomToTop
103 keyNavigationEnabled: false
104 reuseItems: false // do not resue items, otherwise delegates do not hide themselves properly
105 focus: false
106 interactive: false
107
108 add: Transition {
109 id: addAnimation
110 ParallelAnimation {
111 alwaysRunToEnd: true
112 NumberAnimation {
113 property: "opacity"
114 from: 0
115 to: 1
116 duration: Kirigami.Units.longDuration
117 easing.type: Easing.OutCubic
118 }
119 NumberAnimation {
120 property: "y"
121 from: addAnimation.ViewTransition.destination.y - Kirigami.Units.gridUnit * 3
122 duration: Kirigami.Units.longDuration
123 easing.type: Easing.OutCubic
124 }
125 }
126 }
127 displaced: Transition {
128 ParallelAnimation {
129 alwaysRunToEnd: true
130 NumberAnimation {
131 property: "y"
132 duration: Kirigami.Units.longDuration
133 easing.type: Easing.InOutCubic
134 }
135 NumberAnimation {
136 property: "opacity"
137 duration: 0
138 to: 1
139 }
140 }
141 }
142 remove: Transition {
143 ParallelAnimation {
144 alwaysRunToEnd: true
145 NumberAnimation {
146 property: "opacity"
147 from: 1
148 to: 0
149 duration: Kirigami.Units.longDuration
150 easing.type: Easing.InCubic
151 }
152 NumberAnimation {
153 property: "y"
154 to: Kirigami.Units.gridUnit * 3
155 duration: Kirigami.Units.longDuration
156 easing.type: Easing.InCubic
157 }
158 PropertyAction {
159 property: "transformOrigin"
160 value: Item.Bottom
161 }
162 PropertyAnimation {
163 property: "scale"
164 from: 1
165 to: 0
166 duration: Kirigami.Units.longDuration
167 easing.type: Easing.InCubic
168 }
169 }
170 }
171 delegate: QQC2.Control {
172 id: delegate
173
174 hoverEnabled: true
175
176 anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
177 width: Math.min(implicitWidth, maximumNotificationWidth)
178 implicitHeight: {
179 // HACK: contentItem.implicitHeight needs to be updated manually for some reason
180 void contentItem.implicitHeight;
181 return Math.max(implicitBackgroundHeight + topInset + bottomInset,
182 implicitContentHeight + topPadding + bottomPadding);
183 }
184 z: {
185 if (delegate.hovered) {
186 return 2;
187 } else if (delegate.index === 0) {
188 return 1;
189 } else {
190 return 0;
191 }
192 }
193
194 leftPadding: Kirigami.Units.largeSpacing
195 rightPadding: Kirigami.Units.largeSpacing
196 topPadding: Kirigami.Units.largeSpacing
197 bottomPadding: Kirigami.Units.largeSpacing
198
199 contentItem: RowLayout {
200 id: mainLayout
201
202 Kirigami.Theme.inherit: false
203 Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet
204
205 spacing: Kirigami.Units.mediumSpacing
206
207 TapHandler {
208 acceptedButtons: Qt.LeftButton
209 onTapped: eventPoint => hideNotification(index)
210 }
211 Timer {
212 id: timer
213 interval: model.closeInterval
214 running: !delegate.hovered
215 onTriggered: hideNotification(index)
216 }
217
218 QQC2.Label {
219 id: label
220 text: model.text
221 elide: Text.ElideRight
222 wrapMode: Text.Wrap
223 Layout.fillWidth: true
224 Layout.alignment: Qt.AlignVCenter
225 }
226
227 QQC2.Button {
228 id: actionButton
229 text: model.actionButtonText
230 visible: text.length > 0
231 Layout.alignment: Qt.AlignVCenter
232 onClicked: {
233 const callBack = model.callBackWrapper.callBack
234 hideNotification(index)
235 if (callBack && (typeof callBack === "function")) {
236 callBack();
237 }
238 }
239 }
240 }
241 background: Kirigami.ShadowedRectangle {
242 Kirigami.Theme.inherit: false
243 Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet
244 shadow {
245 size: Kirigami.Units.gridUnit/2
246 color: Qt.rgba(0, 0, 0, 0.4)
247 yOffset: 2
248 }
249 radius: Kirigami.Units.cornerRadius
250 color: Kirigami.Theme.backgroundColor
251 opacity: 0.9
252 }
253 }
254 }
255 Component {
256 id: callBackWrapper
257 QtObject {
258 property var callBack
259 }
260 }
261}
262
KIOCORE_EXPORT void add(const QString &fileClass, const QString &directory)
KGuiItem remove()
QString label(StandardShortcut id)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.