@@ -1,5 +1,5 @@
2− * Copyright (C) 2013, 2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -36,44 +36,40 @@ StyledItem {
36 property var type
36 property var type
37 property var hints
37 property var hints
38 property var notification
38 property var notification
39− property color color
39+ property color color: theme.palette.normal.background
40 property bool fullscreen: false
40 property bool fullscreen: false
41 property int maxHeight
41 property int maxHeight
42− property int margins
42+ property int margins: units.gu(1)
43− readonly property bool draggable: (type === Notification.SnapDecision && state === "contracted") || type === Notification.Interactive || type === Notification.Ephemeral
43+
44− readonly property bool darkOnBright: inverseMode || type === Notification.SnapDecision
44+ readonly property real defaultOpacity: 1.0
45− readonly property color sdLightGrey: "#eaeaea"
46− readonly property real contentSpacing: units.gu(2)
47− readonly property bool canBeClosed: type === Notification.Ephemeral
48 property bool hasMouse
45 property bool hasMouse
49− property bool inverseMode
50 property url background: ""
46 property url background: ""
52 objectName: "background"
48 objectName: "background"
53− implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
49+ implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height + shapedBack.anchors.topMargin + margins * 2) : 0
55− color: (type === Notification.Confirmation && notificationList.useModal && !greeter.shown) || darkOnBright ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97)
56− opacity: 1 - (x / notification.width) // FIXME: non-zero initially because of LP: #1354406 workaround, we want this to start at 0 upon creation eventually
51+ // FIXME: non-zero initially because of LP: #1354406 workaround, we want this to start at 0 upon creation eventually
52+ opacity: defaultOpacity - Math.abs(x / notification.width)
58 theme: ThemeSettings {
54 theme: ThemeSettings {
59− name: darkOnBright ? "Ubuntu.Components.Themes.Ambiance" : "Ubuntu.Components.Themes.SuruDark"
55+ name: "Ubuntu.Components.Themes.Ambiance"
62− state: {
58+ readonly property bool expanded: {
63− var result = "";
59+ var result = false;
65− if (type == Notification.SnapDecision) {
61+ if (type === Notification.SnapDecision) {
66− if (ListView.view.currentIndex == index) {
62+ if (ListView.view.currentIndex === index) {
67− result = "expanded";
63+ result = true;
69 if (ListView.view.count > 2) {
65 if (ListView.view.count > 2) {
70− if (ListView.view.currentIndex == -1 && index == 1) {
66+ if (ListView.view.currentIndex === -1 && index == 1) {
71− result = "expanded";
67+ result = true;
73− result = "contracted";
69+ result = false;
76− result = "expanded";
72+ result = true;
@@ -90,72 +86,66 @@ StyledItem {
90 Component.onCompleted: {
86 Component.onCompleted: {
91 // Turn on screen as needed (Powerd.Notification means the screen
87 // Turn on screen as needed (Powerd.Notification means the screen
92 // stays on for a shorter amount of time)
88 // stays on for a shorter amount of time)
93− if (type == Notification.SnapDecision) {
89+ if (type === Notification.SnapDecision) {
94 Powerd.setStatus(Powerd.On, Powerd.SnapDecision);
90 Powerd.setStatus(Powerd.On, Powerd.SnapDecision);
95− } else if (type != Notification.Confirmation) {
91+ } else if (type !== Notification.Confirmation) {
96 Powerd.setStatus(Powerd.On, Powerd.Notification);
92 Powerd.setStatus(Powerd.On, Powerd.Notification);
99 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
95 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
100− if (opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") {
96+ if (opacity == defaultOpacity && hints["suppress-sound"] !== "true" && sound.source !== "") {
101 sound.play();
97 sound.play();
105− Behavior on x {
101+ function closeNotification() {
106− id: normalXBehavior
102+ if (notification.actions.count > 1) { // perform the "reject" action
107−
103+ notification.notification.invokeAction(notification.actions.data(1, ActionModel.RoleActionId));
109− UbuntuNumberAnimation {
110− duration: UbuntuAnimation.FastDuration
111− easing.type: Easing.OutBounce
105+ notification.notification.close();
109+ UbuntuNumberAnimation { easing.type: Easing.OutBounce }
115 onHintsChanged: {
112 onHintsChanged: {
116− if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") {
113+ if (type === Notification.Confirmation && opacity == defaultOpacity && hints["suppress-sound"] !== "true" && sound.source !== "") {
117 sound.play();
114 sound.play();
121 Behavior on height {
118 Behavior on height {
122− id: normalHeightBehavior
124− //enabled: menuItemFactory.progress == 1
126 UbuntuNumberAnimation {
119 UbuntuNumberAnimation {
127 duration: UbuntuAnimation.SnapDuration
120 duration: UbuntuAnimation.SnapDuration
131− states:[
124+ visible: type !== Notification.PlaceHolder
134− PropertyChanges {target: notification; height: units.gu(10)}
138− PropertyChanges {target: notification; height: implicitHeight}
142− clip: fullscreen ? false : true
144− visible: type != Notification.PlaceHolder
126+ BorderImage {
129+ margins: shapedBack.visible ? -units.gu(1) : -units.gu(1.5)
131+ source: "../Stages/graphics/dropshadow2gu.sci"
132+ opacity: notification.opacity * 0.5
133+ enabled: !fullscreen
146 UbuntuShape {
136 UbuntuShape {
147 id: shapedBack
137 id: shapedBack
138+ objectName: "shapedBack"
149 visible: !fullscreen
140 visible: !fullscreen
150 anchors {
141 anchors {
151 fill: parent
142 fill: parent
152 leftMargin: notification.margins
143 leftMargin: notification.margins
153 rightMargin: notification.margins
144 rightMargin: notification.margins
154− topMargin: type === Notification.Confirmation ? units.gu(.5) : 0
145+ topMargin: index == 0 ? notification.margins : 0
156 backgroundColor: parent.color
147 backgroundColor: parent.color
157− opacity: parent.opacity
158− radius: "medium"
148+ radius: "small"
159 aspect: UbuntuShape.Flat
149 aspect: UbuntuShape.Flat
@@ -165,12 +155,11 @@ StyledItem {
165 visible: fullscreen
155 visible: fullscreen
166 anchors.fill: parent
156 anchors.fill: parent
167 color: parent.color
157 color: parent.color
168− opacity: parent.opacity
171 onXChanged: {
160 onXChanged: {
172− if (draggable && notification.x > 0.75 * notification.width) {
161+ if (Math.abs(notification.x) > 0.75 * notification.width) {
173− notification.notification.close()
162+ closeNotification();
@@ -210,22 +199,21 @@ StyledItem {
210 anchors.fill: parent
199 anchors.fill: parent
211 objectName: "interactiveArea"
200 objectName: "interactiveArea"
213− drag.target: draggable ? notification : undefined
202+ drag.target: notification
214 drag.axis: Drag.XAxis
203 drag.axis: Drag.XAxis
215− drag.minimumX: 0
204+ drag.minimumX: -notification.width
216 drag.maximumX: notification.width
205 drag.maximumX: notification.width
218 onClicked: {
208 onClicked: {
219− if (notification.type == Notification.Interactive) {
209+ if (notification.type === Notification.Interactive) {
220 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
210 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
221− } else if (hasMouse && canBeClosed) {
222− notification.notification.close()
224 notificationList.currentIndex = index;
212 notificationList.currentIndex = index;
227 onReleased: {
215 onReleased: {
228− if (notification.x < notification.width / 2) {
216+ if (Math.abs(notification.x) < notification.width / 2) {
229 notification.x = 0
217 notification.x = 0
231 notification.x = notification.width
219 notification.x = notification.width
@@ -233,44 +221,61 @@ StyledItem {
224+ NotificationButton {
225+ objectName: "closeButton"
229+ visible: hasMouse && (containsMouse || interactiveArea.containsMouse)
233+ color: theme.palette.normal.negative
234+ anchors.horizontalCenter: parent.left
235+ anchors.horizontalCenterOffset: notification.parent.state === "narrow" ? notification.margins / 2 : 0
236+ anchors.verticalCenter: parent.top
237+ anchors.verticalCenterOffset: notification.parent.state === "narrow" ? notification.margins / 2 : 0
239+ onClicked: closeNotification();
237 id: outterColumn
243 id: outterColumn
244+ objectName: "outterColumn"
239 anchors {
246 anchors {
240 left: parent.left
247 left: parent.left
241 right: parent.right
248 right: parent.right
242 top: parent.top
249 top: parent.top
243− margins: 0
250+ margins: !fullscreen ? notification.margins : 0
244− topMargin: fullscreen ? 0 : type === Notification.Confirmation ? units.gu(1) : units.gu(2)
247− spacing: type === Notification.Confirmation ? units.gu(1) : units.gu(2)
253+ spacing: notification.margins
250 id: topRow
256 id: topRow
252− spacing: contentSpacing
258+ spacing: notification.margins
253 anchors {
259 anchors {
254 left: parent.left
260 left: parent.left
255 right: parent.right
261 right: parent.right
256− margins: contentSpacing
259 ShapedIcon {
264 ShapedIcon {
262 objectName: "icon"
267 objectName: "icon"
263− width: type == Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6)
268+ width: units.gu(6)
264 height: width
269 height: width
265− shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
270+ shaped: notification.hints["x-canonical-non-shaped-icon"] !== "true"
266− visible: iconSource !== undefined && iconSource !== "" && type !== Notification.Confirmation
271+ visible: iconSource !== "" && type !== Notification.Confirmation
270 id: labelColumn
275 id: labelColumn
271− width: secondaryIcon.visible ? parent.width - x - units.gu(4.5) : parent.width - x
276+ width: secondaryIcon.visible ? parent.width - x - units.gu(3) : parent.width - x
273 anchors.verticalCenter: (icon.visible && !bodyLabel.visible) ? icon.verticalCenter : undefined
277 anchors.verticalCenter: (icon.visible && !bodyLabel.visible) ? icon.verticalCenter : undefined
278+ spacing: units.gu(.4)
276 id: summaryLabel
281 id: summaryLabel
@@ -282,6 +287,8 @@ StyledItem {
283 visible: type !== Notification.Confirmation
288 visible: type !== Notification.Confirmation
284 fontSize: "medium"
289 fontSize: "medium"
290+ font.weight: Font.Light
291+ color: theme.palette.normal.backgroundSecondaryText
285 elide: Text.ElideRight
292 elide: Text.ElideRight
286 textFormat: Text.PlainText
293 textFormat: Text.PlainText
@@ -296,10 +303,13 @@ StyledItem {
297 visible: body != "" && type !== Notification.Confirmation
304 visible: body != "" && type !== Notification.Confirmation
298 fontSize: "small"
305 fontSize: "small"
306+ font.weight: Font.Light
307+ color: theme.palette.normal.backgroundTertiaryText
299 wrapMode: Text.Wrap
308 wrapMode: Text.Wrap
300− maximumLineCount: type == Notification.SnapDecision ? 12 : 2
309+ maximumLineCount: type === Notification.SnapDecision ? 12 : 2
301 elide: Text.ElideRight
310 elide: Text.ElideRight
302 textFormat: Text.PlainText
311 textFormat: Text.PlainText
@@ -307,25 +317,34 @@ StyledItem {
307 id: secondaryIcon
317 id: secondaryIcon
309 objectName: "secondaryIcon"
319 objectName: "secondaryIcon"
310− width: units.gu(3)
320+ width: units.gu(2)
311− height: units.gu(3)
321+ height: width
312 visible: status === Image.Ready
322 visible: status === Image.Ready
313 fillMode: Image.PreserveAspectCrop
323 fillMode: Image.PreserveAspectCrop
317 ListItem.ThinDivider {
327 ListItem.ThinDivider {
318− visible: type == Notification.SnapDecision
328+ visible: type === Notification.SnapDecision && notification.expanded
332+ name: "toolkit_chevron-down_3gu"
333+ visible: type === Notification.SnapDecision && !notification.expanded
336+ anchors.horizontalCenter: parent.horizontalCenter
337+ color: theme.palette.normal.base
321 ShapedIcon {
340 ShapedIcon {
322 id: centeredIcon
341 id: centeredIcon
323 objectName: "centeredIcon"
342 objectName: "centeredIcon"
324− width: units.gu(5)
343+ width: units.gu(4)
325 height: width
344 height: width
326− shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
345+ shaped: notification.hints["x-canonical-non-shaped-icon"] !== "true"
327 fileSource: icon.fileSource
346 fileSource: icon.fileSource
328− visible: fileSource !== undefined && fileSource !== "" && type === Notification.Confirmation
347+ visible: fileSource !== "" && type === Notification.Confirmation
329 anchors.horizontalCenter: parent.horizontalCenter
348 anchors.horizontalCenter: parent.horizontalCenter
@@ -336,44 +355,32 @@ StyledItem {
336 anchors.horizontalCenter: parent.horizontalCenter
355 anchors.horizontalCenter: parent.horizontalCenter
337 visible: type === Notification.Confirmation && body !== ""
356 visible: type === Notification.Confirmation && body !== ""
338 fontSize: "medium"
357 fontSize: "medium"
358+ font.weight: Font.Light
359+ color: theme.palette.normal.backgroundSecondaryText
339 wrapMode: Text.WordWrap
360 wrapMode: Text.WordWrap
340 maximumLineCount: 1
361 maximumLineCount: 1
341 elide: Text.ElideRight
362 elide: Text.ElideRight
342 textFormat: Text.PlainText
363 textFormat: Text.PlainText
345− UbuntuShape {
366+ ProgressBar {
346 id: valueIndicator
367 id: valueIndicator
347 objectName: "valueIndicator"
368 objectName: "valueIndicator"
348 visible: type === Notification.Confirmation
369 visible: type === Notification.Confirmation
349− property double value
370+ minimumValue: 0
350−
371+ maximumValue: 100
372+ showProgressPercentage: false
351 anchors {
373 anchors {
352 left: parent.left
374 left: parent.left
353 right: parent.right
375 right: parent.right
354− margins: contentSpacing
357 height: units.gu(1)
377 height: units.gu(1)
358− backgroundColor: theme.palette.normal.background
359− aspect: UbuntuShape.Flat
364− objectName: "innerBar"
365− width: valueIndicator.width * valueIndicator.value / 100
367− backgroundColor: notification.hints["x-canonical-value-bar-tint"] === "true" ? theme.palette.normal.activity : theme.palette.highlighted.foreground
368− aspect: UbuntuShape.Flat
374 id: dialogColumn
381 id: dialogColumn
375 objectName: "dialogListView"
382 objectName: "dialogListView"
376− spacing: units.gu(2)
383+ spacing: notification.margins
378 visible: count > 0
385 visible: count > 0
@@ -417,12 +424,11 @@ StyledItem {
417 anchors {
424 anchors {
418 left: parent.left
425 left: parent.left
419 right: parent.right
426 right: parent.right
420− margins: contentSpacing
423− spacing: contentSpacing
429+ spacing: notification.margins
425− visible: notification.type === Notification.SnapDecision && oneOverTwoRepeaterTop.count === 3
431+ visible: notification.type === Notification.SnapDecision && oneOverTwoRepeaterTop.count === 3 && notification.expanded
427 Repeater {
433 Repeater {
428 id: oneOverTwoRepeaterTop
434 id: oneOverTwoRepeaterTop
@@ -437,11 +443,13 @@ StyledItem {
437 Component {
443 Component {
438 id: oneOverTwoButtonTop
444 id: oneOverTwoButtonTop
440− Button {
446+ NotificationButton {
441 objectName: "notify_oot_button" + index
447 objectName: "notify_oot_button" + index
442 width: oneOverTwoCase.width
448 width: oneOverTwoCase.width
443 text: oneOverTwoLoaderTop.actionLabel
449 text: oneOverTwoLoaderTop.actionLabel
450+ outline: notification.hints["x-canonical-private-affirmative-tint"] !== "true"
444− color: notification.hints["x-canonical-private-affirmative-tint"] == "true" ? UbuntuColors.green : theme.palette.normal.baseText
451+ color: notification.hints["x-canonical-private-affirmative-tint"] === "true" ? theme.palette.normal.positive
452+ : theme.palette.normal.foreground
445 onClicked: notification.notification.invokeAction(oneOverTwoLoaderTop.actionId)
453 onClicked: notification.notification.invokeAction(oneOverTwoLoaderTop.actionId)
@@ -450,7 +458,7 @@ StyledItem {
453− spacing: contentSpacing
461+ spacing: notification.margins
455 Repeater {
463 Repeater {
456 id: oneOverTwoRepeaterBottom
464 id: oneOverTwoRepeaterBottom
@@ -465,11 +473,13 @@ StyledItem {
465 Component {
473 Component {
466 id: oneOverTwoButtonBottom
474 id: oneOverTwoButtonBottom
468− Button {
476+ NotificationButton {
469 objectName: "notify_oot_button" + index
477 objectName: "notify_oot_button" + index
470− width: oneOverTwoCase.width / 2 - spacing * 2
478+ width: oneOverTwoCase.width / 2 - spacing / 2
471 text: oneOverTwoLoaderBottom.actionLabel
479 text: oneOverTwoLoaderBottom.actionLabel
480+ outline: notification.hints["x-canonical-private-rejection-tint"] !== "true"
472− color: index == 1 && notification.hints["x-canonical-private-rejection-tint"] == "true" ? UbuntuColors.red : theme.palette.normal.baseText
481+ color: index == 1 && notification.hints["x-canonical-private-rejection-tint"] === "true" ? theme.palette.normal.negative
482+ : theme.palette.normal.foreground
473 onClicked: notification.notification.invokeAction(oneOverTwoLoaderBottom.actionId)
483 onClicked: notification.notification.invokeAction(oneOverTwoLoaderBottom.actionId)
@@ -486,10 +496,9 @@ StyledItem {
486 anchors {
496 anchors {
487 left: parent.left
497 left: parent.left
488 right: parent.right
498 right: parent.right
489− margins: contentSpacing
491− visible: notification.type === Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible
500+ visible: notification.type === Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible && notification.expanded
492− spacing: contentSpacing
501+ spacing: notification.margins
493 layoutDirection: Qt.RightToLeft
502 layoutDirection: Qt.RightToLeft
@@ -525,17 +534,19 @@ StyledItem {
525 Component {
534 Component {
526 id: actionButton
535 id: actionButton
528− Button {
537+ NotificationButton {
529 objectName: "notify_button" + index
538 objectName: "notify_button" + index
530− width: buttonRow.width / 2 - spacing * 2
539+ width: buttonRow.width / 2 - spacing / 2
531 text: loader.actionLabel
540 text: loader.actionLabel
541+ outline: (index == 0 && notification.hints["x-canonical-private-affirmative-tint"] !== "true") ||
542+ (index == 1 && notification.hints["x-canonical-private-rejection-tint"] !== "true")
533− var result = theme.palette.normal.baseText;
544+ var result = theme.palette.normal.foreground;
534− if (index == 0 && notification.hints["x-canonical-private-affirmative-tint"] == "true") {
545+ if (index == 0 && notification.hints["x-canonical-private-affirmative-tint"] === "true") {
535− result = UbuntuColors.green;
546+ result = theme.palette.normal.positive;
537− if (index == 1 && notification.hints["x-canonical-private-rejection-tint"] == "true") {
548+ if (index == 1 && notification.hints["x-canonical-private-rejection-tint"] === "true") {
538− result = UbuntuColors.red;
549+ result = theme.palette.normal.negative;
540 return result;
551 return result;
@@ -554,10 +565,9 @@ StyledItem {
554 anchors {
565 anchors {
555 left: parent.left
566 left: parent.left
556 right: parent.right
567 right: parent.right
557− margins: contentSpacing
560− visible: notification.type == Notification.SnapDecision && actionRepeater.count > 3 && !oneOverTwoCase.visible
570+ visible: notification.type === Notification.SnapDecision && actionRepeater.count > 3 && !oneOverTwoCase.visible && notification.expanded
561 model: notification.actions
571 model: notification.actions
562 expanded: false
572 expanded: false
563 startIndex: 2
573 startIndex: 2