on 43b95a0 help sign in
unity8/qml/Notifications/Notification.qml
450 lines 15 kB history raw
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.0
18import QtMultimedia 5.0
19import Ubuntu.Components 1.1
20import Unity.Notifications 1.0
21import QMenuModel 0.1
22import Utils 0.1
24import Ubuntu.Components.ListItems 0.1 as ListItem
26Item {
27 id: notification
29 property alias iconSource: icon.fileSource
30 property alias secondaryIconSource: secondaryIcon.source
31 property alias summary: summaryLabel.text
32 property alias body: bodyLabel.text
33 property var actions
34 property var notificationId
35 property var type
36 property var hints
37 property var notification
38 property color color
39 property bool fullscreen: false
40 property int maxHeight
41 property int margins
42 property Gradient greenGradient : Gradient {
43 GradientStop { position: 0.0; color: "#3fb24f" }
44 GradientStop { position: 1.0; color: "#3fb24f" }
45 }
46 property Gradient darkgreyGradient: Gradient {
47 GradientStop { position: 0.0; color: "#4d4745" }
48 GradientStop { position: 1.0; color: "#4d4745" }
49 }
51 objectName: "background"
52 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : contentColumn.height + contentColumn.spacing * 2) : 0
54 color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
55 opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really
57 state: {
58 var result = "";
60 if (type == Notification.SnapDecision) {
61 if (ListView.view.currentIndex == index) {
62 result = "expanded";
63 } else {
64 if (ListView.view.count > 2) {
65 if (ListView.view.currentIndex == -1 && index == 1) {
66 result = "expanded";
67 } else {
68 result = "contracted";
69 }
70 } else {
71 result = "expanded";
72 }
73 }
74 }
76 return result;
77 }
79 Audio {
80 id: sound
81 objectName: "sound"
82 source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""
83 }
85 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
86 Component.onCompleted: {
87 if (opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source) {
88 sound.play();
89 }
90 }
92 Behavior on height {
93 id: normalHeightBehavior
95 //enabled: menuItemFactory.progress == 1
96 enabled: true
97 SequentialAnimation {
98 PauseAnimation {
99 duration: UbuntuAnimation.SnapDuration
100 }
101 UbuntuNumberAnimation {
102 duration: UbuntuAnimation.SnapDuration
103 }
104 }
105 }
107 states:[
108 State {
109 name: "contracted"
110 PropertyChanges {target: notification; height: units.gu(8)}
111 },
112 State {
113 name: "expanded"
114 PropertyChanges {target: notification; height: implicitHeight}
115 }
116 ]
118 clip: fullscreen ? false : true
120 visible: type != Notification.PlaceHolder
122 UbuntuShape {
123 id: shapedBack
125 visible: !fullscreen
126 anchors {
127 fill: parent
128 leftMargin: notification.margins
129 rightMargin: notification.margins
130 }
131 color: parent.color
132 opacity: parent.opacity
133 radius: "medium"
134 }
136 Rectangle {
137 id: nonShapedBack
139 visible: fullscreen
140 anchors.fill: parent
141 color: parent.color
142 opacity: parent.opacity
143 }
145 Item {
146 id: contents
147 anchors.fill: fullscreen ? nonShapedBack : shapedBack
149 UnityMenuModelPaths {
150 id: paths
152 source: hints["x-canonical-private-menu-model"]
154 busNameHint: "busName"
155 actionsHint: "actions"
156 menuObjectPathHint: "menuPath"
157 }
159 UnityMenuModel {
160 id: unityMenuModel
162 property string lastNameOwner: ""
164 busName: paths.busName
165 actions: paths.actions
166 menuObjectPath: paths.menuObjectPath
167 onNameOwnerChanged: {
168 if (lastNameOwner != "" && nameOwner == "" && notification.notification != undefined) {
169 notification.notification.close()
170 }
171 lastNameOwner = nameOwner
172 }
173 }
175 Behavior on implicitHeight {
176 id: heightBehavior
178 enabled: false
179 UbuntuNumberAnimation {
180 duration: UbuntuAnimation.SnapDuration
181 }
182 }
184 // delay enabling height behavior until the add transition is complete
185 onOpacityChanged: if (opacity == 1) heightBehavior.enabled = true
187 MouseArea {
188 id: interactiveArea
190 anchors.fill: parent
191 objectName: "interactiveArea"
192 onClicked: {
193 if (notification.type == Notification.Interactive) {
194 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
195 } else {
196 notificationList.currentIndex = index;
197 }
198 }
199 }
201 Column {
202 id: contentColumn
203 objectName: "contentColumn"
205 anchors {
206 left: parent.left
207 right: parent.right
208 top: parent.top
209 margins: fullscreen ? 0 : spacing
210 }
212 spacing: units.gu(1)
214 Row {
215 id: topRow
217 spacing: contentColumn.spacing
218 anchors {
219 left: parent.left
220 right: parent.right
221 }
223 ShapedIcon {
224 id: icon
226 objectName: "icon"
227 width: units.gu(6)
228 height: units.gu(6)
229 shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
230 visible: iconSource !== undefined && iconSource != ""
231 }
233 Image {
234 id: secondaryIcon
236 objectName: "secondaryIcon"
237 width: units.gu(2)
238 height: units.gu(2)
239 visible: source !== undefined && source != ""
240 fillMode: Image.PreserveAspectCrop
241 }
243 Column {
244 id: labelColumn
245 width: parent.width - x
247 anchors.verticalCenter: (icon.visible && !bodyLabel.visible) ? icon.verticalCenter : undefined
249 Label {
250 id: summaryLabel
252 objectName: "summaryLabel"
253 anchors {
254 left: parent.left
255 right: parent.right
256 }
257 fontSize: "medium"
258 font.bold: true
259 color: Theme.palette.selected.backgroundText
260 elide: Text.ElideRight
261 textFormat: Text.PlainText
262 }
264 Label {
265 id: bodyLabel
267 objectName: "bodyLabel"
268 anchors {
269 left: parent.left
270 right: parent.right
271 }
272 visible: body != ""
273 fontSize: "small"
274 color: Theme.palette.selected.backgroundText
275 opacity: 0.6
276 wrapMode: Text.WordWrap
277 maximumLineCount: 10
278 elide: Text.ElideRight
279 textFormat: Text.PlainText
280 }
281 }
282 }
284 Column {
285 id: dialogColumn
286 objectName: "dialogListView"
287 spacing: units.gu(2)
289 visible: count > 0
291 anchors {
292 left: parent.left
293 right: parent.right
294 top: fullscreen ? parent.top : undefined
295 bottom: fullscreen ? parent.bottom : undefined
296 }
298 Repeater {
299 model: unityMenuModel
301 NotificationMenuItemFactory {
302 id: menuItemFactory
304 anchors {
305 left: dialogColumn.left
306 right: dialogColumn.right
307 }
309 menuModel: unityMenuModel
310 menuData: model
311 menuIndex: index
312 maxHeight: notification.maxHeight
314 onLoaded: {
315 notification.fullscreen = Qt.binding(function() { return fullscreen; });
316 }
317 onAccepted: {
318 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
319 }
320 }
321 }
322 }
324 Row {
325 id: buttonRow
327 objectName: "buttonRow"
328 anchors {
329 left: parent.left
330 right: parent.right
331 }
332 visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0
333 spacing: units.gu(1)
334 layoutDirection: Qt.RightToLeft
336 Repeater {
337 id: actionRepeater
339 model: notification.actions
340 delegate: Loader {
341 id: loader
343 property string actionId: id
344 property string actionLabel: label
346 Component {
347 id: actionButton
349 Button {
350 objectName: "button" + index
351 width: buttonRow.width / 2 - spacing
352 text: loader.actionLabel
353 gradient: notification.hints["x-canonical-private-button-tint"] == "true" && index == 0 ? greenGradient : darkgreyGradient
354 onClicked: notification.notification.invokeAction(loader.actionId)
355 }
356 }
357 sourceComponent: (index == 0 || index == 1) ? actionButton : undefined
358 }
359 }
360 }
362 ComboButton {
363 id: comboButton
365 objectName: "button2"
366 width: parent.width
367 visible: notification.type == Notification.SnapDecision && actionRepeater.count > 3
368 gradient: darkgreyGradient
369 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
370 expanded: false
371 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
372 comboList: Flickable {
373 // this has to be wrapped inside a flickable
374 // to work around a feature/bug? of the
375 // ComboButton SDK-element, making a regular
376 // unwrapped Column item flickable
377 // see LP: #1332590
378 interactive: false
379 Column {
380 Repeater {
381 id: comboRepeater
383 onVisibleChanged: {
384 comboButton.text = comboRepeater.count >= 3 ? comboRepeater.itemAt(2).actionLabel : ""
385 }
387 model: notification.actions
388 delegate: Loader {
389 id: comboLoader
391 asynchronous: true
392 visible: status == Loader.Ready
393 property string actionId: id
394 property string actionLabel: label
395 readonly property var splitLabel: actionLabel.match(/(^([-a-z0-9]+):)?(.*)$/)
396 Component {
397 id: comboEntry
399 MouseArea {
400 id: comboInputArea
402 objectName: "button" + index
403 width: comboButton.width
404 height: comboIcon.height + units.gu(2)
406 onClicked: {
407 notification.notification.invokeAction(actionId)
408 }
410 ListItem.ThinDivider {
411 visible: index > 3
412 }
414 Icon {
415 id: comboIcon
417 anchors {
418 left: parent.left
419 leftMargin: units.gu(.5)
420 verticalCenter: parent.verticalCenter
421 }
422 width: units.gu(2)
423 height: units.gu(2)
424 color: "white"
425 name: splitLabel[2]
426 }
428 Label {
429 id: comboLabel
431 anchors {
432 left: comboIcon.right
433 leftMargin: units.gu(1)
434 verticalCenter: comboIcon.verticalCenter
435 }
436 fontSize: "small"
437 color: "white"
438 text: splitLabel[3]
439 }
440 }
441 }
442 sourceComponent: (index > 2) ? comboEntry : undefined
443 }
444 }
445 }
446 }
447 }
448 }
449 }