Unity 8
 All Classes Functions Properties
Notification.qml
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  */
16 
17 import QtQuick 2.0
18 import QtMultimedia 5.0
19 import Ubuntu.Components 1.1
20 import Unity.Notifications 1.0
21 import QMenuModel 0.1
22 import Utils 0.1
23 
24 import Ubuntu.Components.ListItems 0.1 as ListItem
25 
26 Item {
27  id: notification
28 
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  }
50 
51  objectName: "background"
52  implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : contentColumn.height + contentColumn.spacing * 2) : 0
53 
54  color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
55  opacity: 0
56 
57  state: {
58  var result = "";
59 
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  }
75 
76  return result;
77  }
78 
79  Audio {
80  id: sound
81  objectName: "sound"
82  source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""
83  }
84 
85  onOpacityChanged: {
86  if (opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source) {
87  sound.play();
88  }
89  }
90 
91  Behavior on height {
92  id: normalHeightBehavior
93 
94  //enabled: menuItemFactory.progress == 1
95  enabled: true
96  SequentialAnimation {
97  PauseAnimation {
98  duration: UbuntuAnimation.SnapDuration
99  }
100  UbuntuNumberAnimation {
101  duration: UbuntuAnimation.SnapDuration
102  }
103  }
104  }
105 
106  states:[
107  State {
108  name: "contracted"
109  PropertyChanges {target: notification; height: units.gu(8)}
110  },
111  State {
112  name: "expanded"
113  PropertyChanges {target: notification; height: implicitHeight}
114  }
115  ]
116 
117  clip: fullscreen ? false : true
118 
119  visible: type != Notification.PlaceHolder
120 
121  UbuntuShape {
122  id: shapedBack
123 
124  visible: !fullscreen
125  anchors {
126  fill: parent
127  leftMargin: notification.margins
128  rightMargin: notification.margins
129  }
130  color: parent.color
131  opacity: parent.opacity
132  radius: "medium"
133  }
134 
135  Rectangle {
136  id: nonShapedBack
137 
138  visible: fullscreen
139  anchors.fill: parent
140  color: parent.color
141  opacity: parent.opacity
142  }
143 
144  Item {
145  id: contents
146  anchors.fill: fullscreen ? nonShapedBack : shapedBack
147 
148  UnityMenuModelPaths {
149  id: paths
150 
151  source: hints["x-canonical-private-menu-model"]
152 
153  busNameHint: "busName"
154  actionsHint: "actions"
155  menuObjectPathHint: "menuPath"
156  }
157 
158  UnityMenuModel {
159  id: unityMenuModel
160 
161  property string lastNameOwner: ""
162 
163  busName: paths.busName
164  actions: paths.actions
165  menuObjectPath: paths.menuObjectPath
166  onNameOwnerChanged: {
167  if (lastNameOwner != "" && nameOwner == "" && notification.notification != undefined) {
168  notification.notification.close()
169  }
170  lastNameOwner = nameOwner
171  }
172  }
173 
174  Behavior on implicitHeight {
175  id: heightBehavior
176 
177  enabled: false
178  UbuntuNumberAnimation {
179  duration: UbuntuAnimation.SnapDuration
180  }
181  }
182 
183  // delay enabling height behavior until the add transition is complete
184  onOpacityChanged: if (opacity == 1) heightBehavior.enabled = true
185 
186  MouseArea {
187  id: interactiveArea
188 
189  anchors.fill: parent
190  objectName: "interactiveArea"
191  onClicked: {
192  if (notification.type == Notification.Interactive) {
193  notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
194  } else {
195  notificationList.currentIndex = index;
196  }
197  }
198  }
199 
200  Column {
201  id: contentColumn
202  objectName: "contentColumn"
203 
204  anchors {
205  left: parent.left
206  right: parent.right
207  top: parent.top
208  margins: fullscreen ? 0 : spacing
209  }
210 
211  spacing: units.gu(1)
212 
213  Row {
214  id: topRow
215 
216  spacing: contentColumn.spacing
217  anchors {
218  left: parent.left
219  right: parent.right
220  }
221 
222  ShapedIcon {
223  id: icon
224 
225  objectName: "icon"
226  width: units.gu(6)
227  height: units.gu(6)
228  shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
229  visible: iconSource !== undefined && iconSource != ""
230  }
231 
232  Image {
233  id: secondaryIcon
234 
235  objectName: "secondaryIcon"
236  width: units.gu(2)
237  height: units.gu(2)
238  visible: source !== undefined && source != ""
239  fillMode: Image.PreserveAspectCrop
240  }
241 
242  Column {
243  id: labelColumn
244  width: parent.width - x
245 
246  anchors.verticalCenter: (icon.visible && !bodyLabel.visible) ? icon.verticalCenter : undefined
247 
248  Label {
249  id: summaryLabel
250 
251  objectName: "summaryLabel"
252  anchors {
253  left: parent.left
254  right: parent.right
255  }
256  fontSize: "medium"
257  font.bold: true
258  color: Theme.palette.selected.backgroundText
259  elide: Text.ElideRight
260  }
261 
262  Label {
263  id: bodyLabel
264 
265  objectName: "bodyLabel"
266  anchors {
267  left: parent.left
268  right: parent.right
269  }
270  visible: body != ""
271  fontSize: "small"
272  color: Theme.palette.selected.backgroundText
273  opacity: 0.6
274  wrapMode: Text.WordWrap
275  maximumLineCount: 10
276  elide: Text.ElideRight
277  }
278  }
279  }
280 
281  Column {
282  id: dialogColumn
283  objectName: "dialogListView"
284  spacing: units.gu(2)
285 
286  visible: count > 0
287 
288  anchors {
289  left: parent.left
290  right: parent.right
291  top: fullscreen ? parent.top : undefined
292  bottom: fullscreen ? parent.bottom : undefined
293  }
294 
295  Repeater {
296  model: unityMenuModel
297 
298  NotificationMenuItemFactory {
299  id: menuItemFactory
300 
301  anchors {
302  left: dialogColumn.left
303  right: dialogColumn.right
304  }
305 
306  menuModel: unityMenuModel
307  menuData: model
308  menuIndex: index
309  maxHeight: notification.maxHeight
310 
311  onLoaded: {
312  notification.fullscreen = Qt.binding(function() { return fullscreen; });
313  }
314  }
315  }
316  }
317 
318  Row {
319  id: buttonRow
320 
321  objectName: "buttonRow"
322  anchors {
323  left: parent.left
324  right: parent.right
325  }
326  visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0
327  spacing: units.gu(1)
328  layoutDirection: Qt.RightToLeft
329 
330  Repeater {
331  id: actionRepeater
332 
333  model: notification.actions
334  delegate: Loader {
335  id: loader
336 
337  property string actionId: id
338  property string actionLabel: label
339 
340  Component {
341  id: actionButton
342 
343  Button {
344  objectName: "button" + index
345  width: buttonRow.width / 2 - spacing
346  text: loader.actionLabel
347  gradient: notification.hints["x-canonical-private-button-tint"] == "true" && index == 0 ? greenGradient : darkgreyGradient
348  onClicked: notification.notification.invokeAction(loader.actionId)
349  }
350  }
351  sourceComponent: (index == 0 || index == 1) ? actionButton : undefined
352  }
353  }
354  }
355 
356  ComboButton {
357  id: comboButton
358 
359  objectName: "button2"
360  width: parent.width
361  visible: notification.type == Notification.SnapDecision && actionRepeater.count > 3
362  gradient: darkgreyGradient
363  onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
364  expanded: false
365  expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
366  comboList: Flickable {
367  // this has to be wrapped inside a flickable
368  // to work around a feature/bug? of the
369  // ComboButton SDK-element, making a regular
370  // unwrapped Column item flickable
371  // see LP: #1332590
372  interactive: false
373  Column {
374  Repeater {
375  id: comboRepeater
376 
377  onVisibleChanged: {
378  comboButton.text = comboRepeater.itemAt(2).actionLabel
379  }
380 
381  model: notification.actions
382  delegate: Loader {
383  id: comboLoader
384 
385  asynchronous: true
386  visible: status == Loader.Ready
387  property string actionId: id
388  property string actionLabel: label
389  readonly property var splitLabel: actionLabel.match(/(^([-a-z0-9]+):)?(.*)$/)
390  Component {
391  id: comboEntry
392 
393  MouseArea {
394  id: comboInputArea
395 
396  objectName: "button" + index
397  width: comboButton.width
398  height: comboIcon.height + units.gu(2)
399 
400  onClicked: {
401  notification.notification.invokeAction(actionId)
402  }
403 
404  ListItem.ThinDivider {
405  visible: index > 3
406  }
407 
408  Icon {
409  id: comboIcon
410 
411  anchors {
412  left: parent.left
413  leftMargin: units.gu(.5)
414  verticalCenter: parent.verticalCenter
415  }
416  width: units.gu(2)
417  height: units.gu(2)
418  color: "white"
419  name: splitLabel[2]
420  }
421 
422  Label {
423  id: comboLabel
424 
425  anchors {
426  left: comboIcon.right
427  leftMargin: units.gu(1)
428  verticalCenter: comboIcon.verticalCenter
429  }
430  fontSize: "small"
431  color: "white"
432  text: splitLabel[3]
433  }
434  }
435  }
436  sourceComponent: (index > 2) ? comboEntry : undefined
437  }
438  }
439  }
440  }
441  }
442  }
443  }
444 }