on xenial help sign in
Using InputInfo, determine if we need swipe or click to dismiss the notifications (e.g. incoming phone call)
commit38784d889d151b598c41f7a756a88e1ce95b23af authorLukáš Tinkl <lukas.tinkl@canonical.com> dateFri 18 Sep 2015, 15:58 (11 years ago) parent98ec58a
12 files changed+206−60
qml/Notifications/Notification.qml+30−25 qml/Notifications/Notifications.qml+3−1 qml/Notifications/SwipeToAct.qml+42−4 qml/OrientedShell.qml+1−1 qml/Shell.qml+2 tests/mocks/Unity/Notifications/MockActionModel.cpp+1−1 tests/mocks/Unity/Notifications/MockActionModel.h+1−1 tests/mocks/Unity/Notifications/MockNotification.cpp+1 tests/mocks/Unity/Notifications/MockNotification.h+1−1 tests/mocks/Unity/Notifications/MockNotificationModel.cpp+5−1 tests/qmltests/Notifications/tst_Notifications.qml+63−10 tests/qmltests/Notifications/tst_SwipeToAct.qml+56−15
qml/Notifications/Notification.qml+30−25
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013, 2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -17,11 +17,11 @@
1717import QtQuick 2.0
1818import QtMultimedia 5.0
1919import Powerd 0.1
20−import Ubuntu.Components 1.1
20+import Ubuntu.Components 1.2
2121import Unity.Notifications 1.0
2222import QMenuModel 0.1
2323import Utils 0.1
24−
24+import "../Components/UnityInputInfo"
2525import Ubuntu.Components.ListItems 0.1 as ListItem
2626
2727Item {
@@ -49,6 +49,8 @@ Item {
4949 readonly property color sdDarkGrey: "#dddddd"
5050 readonly property color sdFontColor: "#5d5d5d"
5151 readonly property real contentSpacing: units.gu(2)
52+ readonly property bool canBeClosed: type === Notification.Ephemeral
53+ property bool clickToClose
5254
5355 objectName: "background"
5456 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
@@ -59,12 +61,12 @@ Item {
5961 state: {
6062 var result = "";
6163
62− if (type == Notification.SnapDecision) {
63− if (ListView.view.currentIndex == index) {
64+ if (type === Notification.SnapDecision) {
65+ if (ListView.view.currentIndex === index) {
6466 result = "expanded";
6567 } else {
6668 if (ListView.view.count > 2) {
67− if (ListView.view.currentIndex == -1 && index == 1) {
69+ if (ListView.view.currentIndex === -1 && index == 1) {
6870 result = "expanded";
6971 } else {
7072 result = "contracted";
@@ -82,7 +84,7 @@ Item {
8284 id: sound
8385 objectName: "sound"
8486 audioRole: MediaPlayer.alert
85− source: hints["suppress-sound"] !== "true" && hints["sound-file"] !== undefined ? hints["sound-file"] : ""
87+ source: hints["suppress-sound"] !== "true" && !!hints["sound-file"] ? hints["sound-file"] : ""
8688 }
8789
8890 Component.onCompleted: {
@@ -139,7 +141,7 @@ Item {
139141
140142 clip: fullscreen ? false : true
141143
142− visible: type != Notification.PlaceHolder
144+ visible: type !== Notification.PlaceHolder
143145
144146 UbuntuShape {
145147 id: shapedBack
@@ -195,7 +197,7 @@ Item {
195197 actions: paths.actions
196198 menuObjectPath: paths.menuObjectPath
197199 onNameOwnerChanged: {
198− if (lastNameOwner !== "" && nameOwner === "" && notification.notification !== undefined) {
200+ if (lastNameOwner !== "" && nameOwner === "" && !!notification.notification) {
199201 notification.notification.close()
200202 }
201203 lastNameOwner = nameOwner
@@ -214,8 +216,10 @@ Item {
214216 drag.maximumX: notification.width
215217
216218 onClicked: {
217− if (notification.type == Notification.Interactive) {
219+ if (notification.type === Notification.Interactive) {
218220 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
221+ } else if (clickToClose && canBeClosed) {
222+ notification.notification.close()
219223 } else {
220224 notificationList.currentIndex = index;
221225 }
@@ -256,10 +260,10 @@ Item {
256260 id: icon
257261
258262 objectName: "icon"
259− width: type == Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6)
263+ width: type === Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6)
260264 height: width
261− shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
262− visible: iconSource !== undefined && iconSource !== "" && type !== Notification.Confirmation
265+ shaped: notification.hints["x-canonical-non-shaped-icon"] === "true" ? false : true
266+ visible: !!iconSource && iconSource !== "" && type !== Notification.Confirmation
263267 }
264268
265269 Column {
@@ -280,7 +284,7 @@ Item {
280284 fontSize: "medium"
281285 color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText
282286 elide: Text.ElideRight
283− textFormat: Text.PlainText
287+ textFormat: Text.StyledText
284288 }
285289
286290 Label {
@@ -295,9 +299,9 @@ Item {
295299 fontSize: "small"
296300 color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText
297301 wrapMode: Text.WordWrap
298− maximumLineCount: type == Notification.SnapDecision ? 12 : 2
302+ maximumLineCount: type === Notification.SnapDecision ? 12 : 2
299303 elide: Text.ElideRight
300− textFormat: Text.PlainText
304+ textFormat: Text.StyledText
301305 }
302306 }
303307
@@ -313,7 +317,7 @@ Item {
313317 }
314318
315319 ListItem.ThinDivider {
316− visible: type == Notification.SnapDecision
320+ visible: type === Notification.SnapDecision
317321 }
318322
319323 ShapedIcon {
@@ -321,9 +325,9 @@ Item {
321325 objectName: "centeredIcon"
322326 width: units.gu(5)
323327 height: width
324− shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
328+ shaped: notification.hints["x-canonical-non-shaped-icon"] === "true" ? false : true
325329 fileSource: icon.fileSource
326− visible: fileSource !== undefined && fileSource !== "" && type === Notification.Confirmation
330+ visible: !!fileSource && fileSource !== "" && type === Notification.Confirmation
327331 anchors.horizontalCenter: parent.horizontalCenter
328332 }
329333
@@ -439,7 +443,7 @@ Item {
439443 objectName: "notify_oot_button" + index
440444 width: oneOverTwoCase.width
441445 text: oneOverTwoLoaderTop.actionLabel
442− color: notification.hints["x-canonical-private-affirmative-tint"] == "true" ? green : sdDarkGrey
446+ color: notification.hints["x-canonical-private-affirmative-tint"] === "true" ? green : sdDarkGrey
443447 onClicked: notification.notification.invokeAction(oneOverTwoLoaderTop.actionId)
444448 }
445449 }
@@ -467,11 +471,11 @@ Item {
467471 objectName: "notify_oot_button" + index
468472 width: oneOverTwoCase.width / 2 - spacing * 2
469473 text: oneOverTwoLoaderBottom.actionLabel
470− color: index == 1 && notification.hints["x-canonical-private-rejection-tint"] == "true" ? red : sdDarkGrey
474+ color: index == 1 && notification.hints["x-canonical-private-rejection-tint"] === "true" ? red : sdDarkGrey
471475 onClicked: notification.notification.invokeAction(oneOverTwoLoaderBottom.actionId)
472476 }
473477 }
474− sourceComponent: (index == 1 || index == 2) ? oneOverTwoButtonBottom : undefined
478+ sourceComponent: (index == 1 || index == 2) ? oneOverTwoButtonBottom : undefined
475479 }
476480 }
477481 }
@@ -499,6 +503,7 @@ Item {
499503 width: buttonRow.width
500504 leftIconName: "call-end"
501505 rightIconName: "call-start"
506+ clickToAct: notification.clickToClose
502507 onRightTriggered: {
503508 notification.notification.invokeAction(notification.actions.data(0, ActionModel.RoleActionId))
504509 }
@@ -528,10 +533,10 @@ Item {
528533 text: loader.actionLabel
529534 color: {
530535 var result = sdDarkGrey;
531− if (index == 0 && notification.hints["x-canonical-private-affirmative-tint"] == "true") {
536+ if (index == 0 && notification.hints["x-canonical-private-affirmative-tint"] === "true") {
532537 result = green;
533538 }
534− if (index == 1 && notification.hints["x-canonical-private-rejection-tint"] == "true") {
539+ if (index == 1 && notification.hints["x-canonical-private-rejection-tint"] === "true") {
535540 result = red;
536541 }
537542 return result;
@@ -554,7 +559,7 @@ Item {
554559 margins: contentSpacing
555560 }
556561
557− visible: notification.type == Notification.SnapDecision && actionRepeater.count > 3 && !oneOverTwoCase.visible
562+ visible: notification.type === Notification.SnapDecision && actionRepeater.count > 3 && !oneOverTwoCase.visible
558563 model: notification.actions
559564 expanded: false
560565 startIndex: 2
qml/Notifications/Notifications.qml+3−1
@@ -28,6 +28,7 @@ ListView {
2828
2929 property real margin
3030 property bool useModal: snapDecisionProxyModel.count > 0
31+ property bool clickToCloseNotifications
3132
3233 UnitySortFilterProxyModel {
3334 id: snapDecisionProxyModel
@@ -52,12 +53,13 @@ ListView {
5253 secondaryIconSource: model.secondaryIcon
5354 summary: model.summary
5455 body: model.body
55− value: model.value
56+ value: model.value ? model.value : -1
5657 actions: model.actions
5758 notificationId: model.id
5859 notification: notificationList.model.getRaw(notificationId)
5960 maxHeight: notificationList.height
6061 margins: notificationList.margin
62+ clickToClose: notificationList.clickToCloseNotifications
6163
6264 // make sure there's no opacity-difference between the several
6365 // elements in a notification
qml/Notifications/SwipeToAct.qml+42−4
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 20014-2015 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -15,7 +15,8 @@
1515 */
1616
1717import QtQuick 2.3
18−import Ubuntu.Components 1.1
18+import Ubuntu.Components 1.2
19+import "../Components/UnityInputInfo"
1920
2021Item {
2122 id: swipeToAct
@@ -28,6 +29,8 @@ Item {
2829 property string leftIconName
2930 property string rightIconName
3031
32+ property bool clickToAct
33+
3134 QtObject {
3235 id: priv
3336
@@ -64,15 +67,47 @@ Item {
6467 }
6568 }
6669
70+ Button {
71+ id: leftButton
72+ objectName: "leftButton"
73+ anchors.verticalCenter: parent.verticalCenter
74+ anchors.left: parent.left
75+ iconName: leftIconName
76+ visible: clickToAct
77+ width: (parent.width / 2) - priv.gap
78+ height: leftShape.height
79+ color: UbuntuColors.red
80+ onClicked: {
81+ leftTriggered()
82+ }
83+ }
84+
85+ Button {
86+ id: rightButton
87+ objectName: "rightButton"
88+ anchors.verticalCenter: parent.verticalCenter
89+ anchors.right: parent.right
90+ iconName: rightIconName
91+ visible: clickToAct
92+ width: (parent.width / 2) - priv.gap
93+ height: rightShape.height
94+ color: UbuntuColors.green
95+ onClicked: {
96+ rightTriggered()
97+ }
98+ }
99+
67100 UbuntuShape {
68101 id: row
69102 width: parent.width
70103 height: priv.sliderHeight
71104 color: priv.sliderBGColor
72105 borderSource: "none"
106+ visible: !clickToAct
73107
74108 UbuntuShape {
75109 id: leftShape
110+ objectName: "leftArea"
76111 anchors.top: parent.top
77112 anchors.left: parent.left
78113 anchors.margins: priv.gap
@@ -91,6 +126,7 @@ Item {
91126 height: units.gu(3.5)
92127 name: leftIconName
93128 color: "white"
129+
94130 }
95131 }
96132
@@ -142,8 +178,7 @@ Item {
142178
143179 onXChanged: {
144180 var factor
145− if (slider.x <= priv.gap + leftShape.width)
146− {
181+ if (slider.x <= priv.gap + leftShape.width) {
147182 factor = (slider.x - priv.gap) / leftShape.width
148183 slider.color = priv.interpolate(leftShape.color, priv.sliderMainColor, factor)
149184 } else if (slider.x >= rightShape.x - slider.width) {
@@ -167,6 +202,7 @@ Item {
167202 color: "white"
168203 }
169204 }
205+
170206 Row {
171207 anchors.verticalCenter: parent.verticalCenter
172208 anchors.left: slider.right
@@ -189,6 +225,7 @@ Item {
189225
190226 UbuntuShape {
191227 id: rightShape
228+ objectName: "rightArea"
192229 anchors.top: parent.top
193230 anchors.right: parent.right
194231 anchors.margins: priv.gap
@@ -214,6 +251,7 @@ Item {
214251 MouseArea {
215252 id: mouseArea
216253 objectName: "swipeMouseArea"
254+ enabled: !clickToAct
217255
218256 anchors.fill: row
219257 drag.target: slider
qml/OrientedShell.qml+1−1
@@ -44,7 +44,6 @@ Rectangle {
4444 name: applicationArguments.deviceName
4545 }
4646
47−
4847 // to be overwritten by tests
4948 property var unity8Settings: Unity8Settings {}
5049 property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
@@ -155,6 +154,7 @@ Rectangle {
155154 nativeWidth: root.width
156155 nativeHeight: root.height
157156 mode: applicationArguments.mode
157+ clickToCloseNotifications: UnityInputInfo.mice > deviceConfiguration.ignoredMice
158158
159159 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
160160 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
qml/Shell.qml+2
@@ -63,6 +63,7 @@ Item {
6363 function updateFocusedAppOrientationAnimated() {
6464 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
6565 }
66+ property bool clickToCloseNotifications
6667
6768 // to be read from outside
6869 readonly property int mainAppWindowOrientationAngle:
@@ -636,6 +637,7 @@ Item {
636637
637638 model: NotificationBackend.Model
638639 margin: units.gu(1)
640+ clickToCloseNotifications: shell.clickToCloseNotifications
639641
640642 y: topmostIsFullscreen ? 0 : panel.panelHeight
641643 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
tests/mocks/Unity/Notifications/MockActionModel.cpp+1−1
@@ -59,7 +59,7 @@ QHash<int, QByteArray> ActionModel::roleNames() const {
5959 return roles;
6060}
6161
62−Q_INVOKABLE QVariant ActionModel::data(int row, int role) const
62+QVariant ActionModel::data(int row, int role) const
6363{
6464 return data(index(row, 0), role);
6565}
tests/mocks/Unity/Notifications/MockActionModel.h+1−1
@@ -33,7 +33,7 @@ public:
3333 virtual ~ActionModel();
3434
3535 int rowCount(const QModelIndex &index) const override;
36− QVariant data(const QModelIndex &index, int role) const override;
36+ Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override; // invokable for testing
3737 QHash<int, QByteArray> roleNames() const override;
3838
3939 Q_ENUMS(ActionsRoles)
tests/mocks/Unity/Notifications/MockNotification.cpp+1
@@ -39,6 +39,7 @@ MockNotification::MockNotification(QObject *parent) : QObject(parent), p(new Moc
3939}
4040
4141MockNotification::~MockNotification() {
42+ delete p->actionsModel;
4243}
4344
4445QString MockNotification::getSummary() const {
tests/mocks/Unity/Notifications/MockNotification.h+1−1
@@ -30,7 +30,7 @@ struct MockNotificationPrivate;
3030
3131class MockNotification : public QObject {
3232 Q_OBJECT
33− Q_ENUMS(Type)
33+ Q_ENUMS(Type Urgency)
3434 Q_PROPERTY(QString summary READ getSummary WRITE setSummary NOTIFY summaryChanged)
3535 Q_PROPERTY(QString body READ getBody WRITE setBody NOTIFY bodyChanged)
3636 Q_PROPERTY(int nid READ getID WRITE setID NOTIFY idChanged)
tests/mocks/Unity/Notifications/MockNotificationModel.cpp+5−1
@@ -35,6 +35,10 @@ MockNotificationModel::MockNotificationModel(QObject *parent) : QAbstractListMod
3535}
3636
3737MockNotificationModel::~MockNotificationModel() {
38+ Q_FOREACH(MockNotification *n, m_queue) {
39+ n->deleteLater();
40+ }
41+ m_queue.clear();
3842}
3943
4044int MockNotificationModel::rowCount(const QModelIndex &) const {
@@ -47,7 +51,7 @@ int MockNotificationModel::getCount() const {
4751
4852QVariant MockNotificationModel::data(const QModelIndex &index, int role) const {
4953 if (!index.isValid())
50− return QVariant();
54+ return QVariant();
5155
5256 switch(role) {
5357 case ModelInterface::RoleType:
tests/qmltests/Notifications/tst_Notifications.qml+63−10
@@ -12,16 +12,15 @@
1212 *
1313 * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Mirco Mueller <mirco.mueller@canonical.com>
1815 */
1916
20−import QtQuick 2.0
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
2119import QtTest 1.0
2220import ".."
2321import "../../../qml/Notifications"
24−import Ubuntu.Components 0.1
22+import "../../../qml/Components/UnityInputInfo"
23+import Ubuntu.Components 1.2
2524import Unity.Test 0.1
2625import Unity.Notifications 1.0
2726import QtMultimedia 5.0
@@ -77,7 +76,7 @@ Item {
7776 "type": Notification.Ephemeral,
7877 "hints": {},
7978 "summary": "Cole Raby",
80− "body": "I did not expect it to be that late.",
79+ "body": "I did not expect it to be <b>that</b> late.",
8180 "icon": "../graphics/avatars/amanda.png",
8281 "secondaryIcon": "../graphics/applicationIcons/facebook.png",
8382 "rawActions": ["reply_id", "Dummy"]})
@@ -91,7 +90,7 @@ Item {
9190 "type": Notification.Ephemeral,
9291 "hints": {"x-canonical-non-shaped-icon": "true"},
9392 "summary": "Contacts",
94− "body": "Synchronised contacts-database with cloud-storage.",
93+ "body": "Synchronised contacts-database &amp; cloud-storage.",
9594 "icon": "../graphics/applicationIcons/contacts-app.png",
9695 "secondaryIcon": "",
9796 "rawActions": ["reply_id", "Dummy"]})
@@ -189,6 +188,7 @@ Item {
189188
190189 anchors.fill: parent
191190 model: mockModel
191+ clickToCloseNotifications: UnityInputInfo.mice > 0 // for testing, we add/remove only a mock mouse
192192 }
193193 }
194194
@@ -218,7 +218,7 @@ Item {
218218
219219 Button {
220220 width: parent.width
221− text: "add an non-shaped-icon-summary-body"
221+ text: "add a non-shaped-icon-summary-body"
222222 onClicked: rootRow.addEphemeralNonShapedIconNotification()
223223 }
224224
@@ -257,6 +257,24 @@ Item {
257257 text: "clear model"
258258 onClicked: rootRow.clearNotifications()
259259 }
260+
261+ RowLayout {
262+ Layout.fillWidth: true
263+ CheckBox {
264+ id: fakeMouseCB
265+ onClicked: {
266+ if (checked) {
267+ UnityInputInfo.inputInfo.addMockMouse();
268+ } else {
269+ UnityInputInfo.inputInfo.removeMockMouse();
270+ }
271+ }
272+ }
273+ Label {
274+ text: "With fake mouse"
275+ anchors.verticalCenter: parent.verticalCenter
276+ }
277+ }
260278 }
261279 }
262280
@@ -560,7 +578,7 @@ Item {
560578 waitForRendering(notifications);
561579
562580 var notification = findChild(notifications, "notification" + (mockModel.count - 1))
563− verify(notification !== undefined, "notification wasn't found");
581+ verify(!!notification, "notification wasn't found");
564582
565583 waitForRendering(notification);
566584 tryCompare(notification, "height", notification.state === "contracted" ? units.gu(10) : notification.implicitHeight);
@@ -654,7 +672,7 @@ Item {
654672 actionSpy.clear()
655673
656674 // click to collapse
657− tryCompareFunction(function() { mouseClick(comboButton, comboButton.width / 2, comboButton.height / 2); return comboButton.expanded == false; }, true);
675+ tryCompareFunction(function() { mouseClick(comboButton, comboButton.width / 2, comboButton.height / 2); return comboButton.expanded === false; }, true);
658676 } else {
659677 mouseClick(buttonCancel)
660678 compare(actionSpy.signalArguments[0][0], data.n.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action")
@@ -675,6 +693,41 @@ Item {
675693 tryCompare(mockModel, "count", before - 1)
676694 }
677695 }
696+
697+ function test_clickToClose_data() { // reuse the data
698+ UnityInputInfo.inputInfo.addMockMouse();
699+ return test_NotificationRenderer_data();
700+ }
701+
702+ function test_clickToClose(data) {
703+ // populate model with some mock notifications
704+ mockModel.append(data.n)
705+
706+ // make sure the view is properly updated before going on
707+ notifications.forceLayout();
708+ waitForRendering(notifications);
709+
710+ var notification = findChild(notifications, "notification" + (mockModel.count - 1))
711+ verify(!!notification, "notification wasn't found");
712+
713+ // click-to-dismiss check
714+ waitForRendering(notification);
715+ var before = mockModel.count;
716+ var canBeClosed = notification.canBeClosed;
717+ mouseClick(notification);
718+
719+ if (canBeClosed) {
720+ // closing the notification, the model count should be one less
721+ tryCompare(mockModel, "count", before - 1)
722+ } else {
723+ // not closing, model stays the same
724+ tryCompare(mockModel, "count", before)
725+ }
726+ }
727+
728+ function cleanupTestCase() {
729+ UnityInputInfo.inputInfo.removeMockMouse();
730+ }
678731 }
679732 }
680733}
tests/qmltests/Notifications/tst_SwipeToAct.qml+56−15
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,10 +14,12 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
1819import QtTest 1.0
1920import ".."
2021import "../../../qml/Notifications"
22+import "../../../qml/Components/UnityInputInfo"
2123import Ubuntu.Components 0.1
2224import Unity.Test 0.1
2325import Unity.Notifications 1.0
@@ -97,7 +99,6 @@ Item {
9799
98100 MouseArea{
99101 id: clickThroughCatcher
100−
101102 anchors.fill: parent
102103 }
103104
@@ -108,6 +109,7 @@ Item {
108109
109110 anchors.fill: parent
110111 model: mockModel
112+ clickToCloseNotifications: UnityInputInfo.mice > 0 // for testing, we add/remove only a mock mouse
111113 }
112114 }
113115
@@ -140,6 +142,24 @@ Item {
140142 text: "clear model"
141143 onClicked: rootRow.clearNotifications()
142144 }
145+
146+ RowLayout {
147+ Layout.fillWidth: true
148+ CheckBox {
149+ id: fakeMouseCB
150+ onClicked: {
151+ if (checked) {
152+ UnityInputInfo.inputInfo.addMockMouse();
153+ } else {
154+ UnityInputInfo.inputInfo.removeMockMouse();
155+ }
156+ }
157+ }
158+ Label {
159+ text: "With fake mouse"
160+ anchors.verticalCenter: parent.verticalCenter
161+ }
162+ }
143163 }
144164 }
145165
@@ -227,7 +247,7 @@ Item {
227247 waitForRendering(notifications);
228248
229249 var notification = findChild(notifications, "notification" + (mockModel.count - 1))
230− verify(notification !== undefined, "notification wasn't found");
250+ verify(!!notification, "notification wasn't found");
231251
232252 waitForRendering(notification);
233253
@@ -245,7 +265,7 @@ Item {
245265
246266 // test input does not fall through
247267 mouseClick(notification)
248− if(data.type == Notification.Interactive) {
268+ if(data.type === Notification.Interactive) {
249269 actionSpy.wait()
250270 compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id for interactive action")
251271 }
@@ -258,21 +278,42 @@ Item {
258278
259279 if(data.buttonRowVisible) {
260280 var swipeButton = findChild(buttonRow, "notify_swipe_button")
261− var slider = findChild(swipeButton, "slider")
262− var swipeMouseArea = findChild(swipeButton, "swipeMouseArea")
263− var x = swipeMouseArea.width / 2
264− var y = swipeMouseArea.height / 2
281+
282+ if (!swipeButton.clickToAct) { // don't run if there's a real mouse
283+ var slider = findChild(swipeButton, "slider")
284+ var swipeMouseArea = findChild(swipeButton, "swipeMouseArea")
285+ var x = swipeMouseArea.width / 2
286+ var y = swipeMouseArea.height / 2
287+
288+ if(data.checkSwipeToActAccept) {
289+ tryCompareFunction(function() { mouseDrag(slider, x, y, (swipeMouseArea.width / 2) - slider.width, 0); return actionSpy.signalArguments.length > 0; }, true);
290+ compare(actionSpy.signalArguments[0][0], data.actions.data(0, ActionModel.RoleActionId), "got wrong id for positive action");
291+ actionSpy.clear();
292+ }
293+ if(data.checkSwipeToActReject) {
294+ tryCompareFunction(function() { mouseDrag(slider, x, y, -(swipeMouseArea.width / 2), 0); return actionSpy.signalArguments.length > 0; }, true);
295+ compare(actionSpy.signalArguments[0][0], data.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action");
296+ actionSpy.clear();
297+ }
298+ }
299+
300+ // add a mock mouse, test clicking the left/right buttons
301+ UnityInputInfo.inputInfo.addMockMouse();
302+ var leftButton = findChild(swipeButton, "leftButton");
303+ var rightButton = findChild(swipeButton, "rightButton");
265304
266305 if(data.checkSwipeToActAccept) {
267− tryCompareFunction(function() { mouseDrag(slider, x, y, (swipeMouseArea.width / 2) - slider.width, 0); return actionSpy.signalArguments.length > 0; }, true);
268− compare(actionSpy.signalArguments[0][0], data.actions.data(0, ActionModel.RoleActionId), "got wrong id for positive action")
269− actionSpy.clear()
306+ mouseClick(rightButton);
307+ compare(actionSpy.signalArguments[0][0], data.actions.data(0, ActionModel.RoleActionId), "got wrong id for positive action");
308+ actionSpy.clear();
270309 }
310+
271311 if(data.checkSwipeToActReject) {
272− tryCompareFunction(function() { mouseDrag(slider, x, y, -(swipeMouseArea.width / 2), 0); return actionSpy.signalArguments.length > 0; }, true);
273− compare(actionSpy.signalArguments[0][0], data.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action")
274− actionSpy.clear()
312+ mouseClick(leftButton);
313+ compare(actionSpy.signalArguments[0][0], data.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action");
314+ actionSpy.clear();
275315 }
316+ UnityInputInfo.inputInfo.removeMockMouse();
276317 }
277318 }
278319 }