Merge lp:~dandrader/unity/phablet_close_apps_from_dash into lp:unity/phablet

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Daniel d'Andrada
Approved revision: no longer in the source branch.
Merged at revision: 619
Proposed branch: lp:~dandrader/unity/phablet_close_apps_from_dash
Merge into: lp:unity/phablet
Diff against target: 654 lines (+458/-11)
13 files modified
Components/ApplicationManagerWrapper.qml (+4/-0)
Components/ResponsiveFlowView.qml (+1/-0)
Dash/Apps/CloseIcon.qml (+62/-0)
Dash/Apps/RunningApplicationTile.qml (+41/-3)
Dash/Apps/RunningApplicationsGrid.qml (+30/-1)
Dash/DashApps.qml (+15/-0)
tests/mocks/Ubuntu/Application/ApplicationListModel.cpp (+62/-0)
tests/mocks/Ubuntu/Application/ApplicationListModel.h (+16/-4)
tests/mocks/Ubuntu/Application/ApplicationManager.cpp (+19/-0)
tests/mocks/Ubuntu/Application/ApplicationManager.h (+1/-1)
tests/mocks/Ubuntu/Application/plugin.cpp (+1/-2)
tests/qmltests/CMakeLists.txt (+1/-0)
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml (+205/-0)
To merge this branch: bzr merge lp:~dandrader/unity/phablet_close_apps_from_dash
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Albert Astals Cid (community) Approve
Review via email: mp+159845@code.launchpad.net

Commit message

Close applications using their thumbnails on the Dash

Long-press on a application thumbnail to bring up the close mode
(close icon decorations will pop up). Then tap on the thumbnail
of the application you want to close to close it.

You can dismiss the close/termination mode in two ways:
1 - by long-pressing one of the app thumbnails
2 - by sliding horizontally to another dash.

As there's no design for this feature yet I didn't to go any further/deeper
on it.

Description of the change

Close applications using their thumbnails on the Dash

Long-press on a application thumbnail to bring up the close mode (close icon decorations will pop up). Then tap on the thumbnail of the application you want to close to close it.

You can dismiss the close/termination mode in two ways:
1 - by long-pressing one of the app thumbnails
2 - by sliding horizontally to another dash.

As there's no design for this feature yet I didn't to go any further/deeper on it.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

As discussed in IRC it'd be good to have a way to remove the 800 and make sure that the termination properties are disabled until the pressAndHold signal is triggered, suggested to use a signalspy and the tryCompareFunction thing

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Dash/Apps/CloseIcon.qml UNKNOWN *No copyright*

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Text conflict in tests/qmltests/CMakeLists.txt

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

All fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Looks good

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Components/ApplicationManagerWrapper.qml'
2--- Components/ApplicationManagerWrapper.qml 2013-04-09 12:03:53 +0000
3+++ Components/ApplicationManagerWrapper.qml 2013-04-22 15:40:50 +0000
4@@ -54,6 +54,10 @@
5 }
6 }
7
8+ function stopProcess(application) {
9+ ApplicationManager.stopProcess(application)
10+ }
11+
12 function focusApplication(application) {
13 if (application == null || application == undefined) {
14 return;
15
16=== modified file 'Components/ResponsiveFlowView.qml'
17--- Components/ResponsiveFlowView.qml 2013-04-09 15:52:21 +0000
18+++ Components/ResponsiveFlowView.qml 2013-04-22 15:40:50 +0000
19@@ -32,6 +32,7 @@
20 property alias delegate: repeater1.delegate
21 readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing
22 readonly property int cellHeight: referenceDelegateWidth + verticalSpacing
23+ property alias move: flow.move
24
25 height: flow.height + flow.anchors.topMargin
26
27
28=== added file 'Dash/Apps/CloseIcon.qml'
29--- Dash/Apps/CloseIcon.qml 1970-01-01 00:00:00 +0000
30+++ Dash/Apps/CloseIcon.qml 2013-04-22 15:40:50 +0000
31@@ -0,0 +1,62 @@
32+/*
33+ * Copyright (C) 2013 Canonical, Ltd.
34+ *
35+ * This program is free software; you can redistribute it and/or modify
36+ * it under the terms of the GNU General Public License as published by
37+ * the Free Software Foundation; version 3.
38+ *
39+ * This program is distributed in the hope that it will be useful,
40+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
41+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42+ * GNU General Public License for more details.
43+ *
44+ * You should have received a copy of the GNU General Public License
45+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
46+ */
47+
48+import QtQuick 2.0
49+
50+Item {
51+ id: root
52+
53+ Image {
54+ id: closeIcon
55+ anchors.centerIn: parent
56+ source: "graphics/close_btn.png"
57+
58+ state: (root.enabled) ? "shown" : "hidden"
59+
60+ states: [
61+ State {
62+ name: "shown"
63+ PropertyChanges {
64+ target: closeIcon
65+ height: root.height
66+ width: root.width
67+ }
68+ },
69+ State {
70+ name: "hidden"
71+ PropertyChanges {
72+ target: closeIcon
73+ height: 0
74+ width: 0
75+ }
76+ }
77+
78+ ]
79+ transitions: [
80+ Transition {
81+ to: "shown"
82+ NumberAnimation {
83+ properties: "width, height"; duration: 300
84+ easing { type: Easing.OutBack; overshoot: 5 }
85+ }
86+ },
87+ Transition {
88+ to: "hidden"
89+ NumberAnimation { properties: "width, height"; duration: 250; }
90+ }
91+ ]
92+ }
93+}
94
95=== modified file 'Dash/Apps/RunningApplicationTile.qml'
96--- Dash/Apps/RunningApplicationTile.qml 2013-02-26 13:51:48 +0000
97+++ Dash/Apps/RunningApplicationTile.qml 2013-04-22 15:40:50 +0000
98@@ -26,11 +26,32 @@
99 property var application
100 property bool __sideStageEnabled: shell.applicationManager.sideStageEnabled
101
102- height: childrenRect.height
103+ signal requestedApplicationActivation(var application)
104+ signal requestedApplicationTermination(var application)
105+ signal requestedActivationMode()
106+ signal requestedTerminationMode()
107+
108+ // Was: childrenRect.height
109+ // To avoid "binding loop" warning
110+ height: shapedApplicationImage.height + labelContainer.height
111+
112 width: shapedApplicationImage.width
113
114+ property bool terminationModeEnabled: false
115+
116 onClicked: {
117- shell.activateApplication(application.desktopFile)
118+ if (terminationModeEnabled)
119+ requestedApplicationTermination(application)
120+ else
121+ requestedApplicationActivation(application)
122+ }
123+
124+ onPressAndHold: {
125+ if (terminationModeEnabled) {
126+ requestedActivationMode()
127+ } else {
128+ requestedTerminationMode()
129+ }
130 }
131
132 function updateScreenshotFromCache() {
133@@ -44,7 +65,11 @@
134 top: parent.top
135 horizontalCenter: parent.horizontalCenter
136 }
137- width: (application.stage === ApplicationsModel.MainStage && __sideStageEnabled) ? units.gu(22) : units.gu(11)
138+
139+ // FIXME: width and height should be defined according to the
140+ // application window's aspect ratio.
141+ width: (application.stage === ApplicationsModel.MainStage && __sideStageEnabled) ?
142+ units.gu(22) : units.gu(11)
143 height: (__sideStageEnabled) ? units.gu(22) : units.gu(19)
144 radius: "medium"
145 image: applicationImage
146@@ -98,4 +123,17 @@
147 horizontalAlignment: Text.AlignHCenter
148 }
149 }
150+
151+ CloseIcon {
152+ anchors {
153+ right: shapedApplicationImage.right
154+ rightMargin: -units.gu(1)
155+ top: parent.top
156+ topMargin: -units.gu(1)
157+ }
158+ height: units.gu(6)
159+ width: units.gu(6)
160+ id: closeIcon
161+ enabled: root.terminationModeEnabled
162+ }
163 }
164
165=== modified file 'Dash/Apps/RunningApplicationsGrid.qml'
166--- Dash/Apps/RunningApplicationsGrid.qml 2013-04-18 13:04:23 +0000
167+++ Dash/Apps/RunningApplicationsGrid.qml 2013-04-22 15:40:50 +0000
168@@ -30,6 +30,16 @@
169
170 Behavior on height { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
171
172+ property bool canEnableTerminationMode: true
173+
174+ onCanEnableTerminationModeChanged: {
175+ if (!canEnableTerminationMode)
176+ terminationModeEnabled = false
177+ }
178+
179+ // when false, it means it's on activation mode
180+ property bool terminationModeEnabled: false
181+
182 maximumNumberOfColumns: 10
183 minimumHorizontalSpacing: units.gu(2)
184 referenceDelegateWidth: units.gu(11)
185@@ -41,15 +51,34 @@
186
187 RunningApplicationTile {
188 id: runningAppTile
189+ objectName: "runningAppTile " + model.application.name
190 anchors {
191 top: parent.top
192 horizontalCenter: parent.horizontalCenter
193 }
194 application: model.application
195+ onRequestedActivationMode: { root.terminationModeEnabled = false }
196+ onRequestedTerminationMode: {
197+ if (canEnableTerminationMode)
198+ root.terminationModeEnabled = true
199+ }
200+ onRequestedApplicationTermination: {
201+ shell.applicationManager.stopProcess(application)
202+ }
203+ onRequestedApplicationActivation: {
204+ shell.activateApplication(application.desktopFile)
205+ }
206+
207+ terminationModeEnabled: root.terminationModeEnabled
208+
209 Component.onCompleted: {
210 root.updateScreenshots.connect(updateScreenshotFromCache);
211 }
212- //FIXME: add animation on item removal
213 }
214 }
215+
216+ move: Transition {
217+ NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutCubic }
218+ }
219 }
220+
221
222=== added directory 'Dash/Apps/graphics'
223=== added file 'Dash/Apps/graphics/close_btn@9.png'
224Binary files Dash/Apps/graphics/close_btn@9.png 1970-01-01 00:00:00 +0000 and Dash/Apps/graphics/close_btn@9.png 2013-04-22 15:40:50 +0000 differ
225=== modified file 'Dash/DashApps.qml'
226--- Dash/DashApps.qml 2013-04-19 16:50:39 +0000
227+++ Dash/DashApps.qml 2013-04-22 15:40:50 +0000
228@@ -129,12 +129,27 @@
229 if (modelName == "RunningApplicationsModel") {
230 item.firstModel = mainStageApplicationsModel
231 item.secondModel = sideStageApplicationModel
232+ item.canEnableTerminationMode =
233+ Qt.binding(function() { return isCurrent; })
234 } else {
235 item.model = categoryModels[modelName]
236 }
237 }
238 asynchronous: true
239 }
240+
241+ ListView.onRemove: SequentialAnimation {
242+ PropertyAction {
243+ target: container; property: "ListView.delayRemove"; value: true
244+ }
245+ NumberAnimation {
246+ target: container; property: "height"; to: 0;
247+ duration: 250; easing.type: Easing.InOutQuad
248+ }
249+ PropertyAction {
250+ target: container; property: "ListView.delayRemove"; value: false
251+ }
252+ }
253 }
254
255 sectionProperty: "category"
256
257=== modified file 'tests/mocks/Ubuntu/Application/ApplicationListModel.cpp'
258--- tests/mocks/Ubuntu/Application/ApplicationListModel.cpp 2013-04-18 12:47:12 +0000
259+++ tests/mocks/Ubuntu/Application/ApplicationListModel.cpp 2013-04-22 15:40:50 +0000
260@@ -80,3 +80,65 @@
261 Q_EMIT countChanged();
262 }
263 }
264+
265+bool ApplicationListModel::contains(Application* application) const {
266+ return m_applications.contains(application);
267+}
268+
269+void ApplicationListModel::clear()
270+{
271+ beginRemoveRows(QModelIndex(), 0, m_applications.size()-1);
272+ m_applications.clear();
273+ endRemoveRows();
274+ Q_EMIT countChanged();
275+}
276+
277+QQmlListProperty<Application> ApplicationListModel::applications()
278+{
279+ return QQmlListProperty<Application>(this, 0,
280+ &ApplicationListModel::appendApplication,
281+ &ApplicationListModel::countApplications,
282+ &ApplicationListModel::atApplication,
283+ &ApplicationListModel::clearApplications);
284+}
285+
286+void ApplicationListModel::appendApplication(QQmlListProperty<Application> *list,
287+ Application *application)
288+{
289+ ApplicationListModel *self = qobject_cast<ApplicationListModel *>(list->object);
290+ if (self) {
291+ application->setParent(self);
292+ self->add(application);
293+ }
294+}
295+
296+int ApplicationListModel::countApplications(QQmlListProperty<Application> *list)
297+{
298+ ApplicationListModel *self = qobject_cast<ApplicationListModel *>(list->object);
299+ if (self) {
300+ return self->m_applications.size();
301+ } else {
302+ return 0;
303+ }
304+}
305+
306+Application* ApplicationListModel::atApplication(QQmlListProperty<Application> *list,
307+ int index)
308+{
309+ ApplicationListModel *self = qobject_cast<ApplicationListModel *>(list->object);
310+ if (!self) { return 0; }
311+
312+ if (index >= 0 && index < self->m_applications.size()) {
313+ return self->m_applications.at(index);
314+ } else {
315+ return 0;
316+ }
317+}
318+
319+void ApplicationListModel::clearApplications(QQmlListProperty<Application> *list)
320+{
321+ ApplicationListModel *self = qobject_cast<ApplicationListModel *>(list->object);
322+ if (self) {
323+ self->clear();
324+ }
325+}
326
327=== modified file 'tests/mocks/Ubuntu/Application/ApplicationListModel.h'
328--- tests/mocks/Ubuntu/Application/ApplicationListModel.h 2013-04-18 12:47:12 +0000
329+++ tests/mocks/Ubuntu/Application/ApplicationListModel.h 2013-04-22 15:40:50 +0000
330@@ -18,14 +18,17 @@
331 #define APPLICATION_LIST_MODEL_H
332
333 #include <QAbstractListModel>
334+#include <QQmlListProperty>
335+#include "Application.h"
336
337-class Application;
338 class ApplicationManager;
339
340 class ApplicationListModel : public QAbstractListModel {
341 Q_OBJECT
342
343 Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
344+ Q_PROPERTY(QQmlListProperty<Application> applications READ applications)
345+ Q_CLASSINFO("DefaultProperty", "applications")
346
347 public:
348 explicit ApplicationListModel(QObject* parent = 0);
349@@ -38,15 +41,24 @@
350 Q_INVOKABLE QVariant get(int index) const;
351 Q_INVOKABLE void move(int from, int to);
352
353+ QQmlListProperty<Application> applications();
354+
355+ Q_INVOKABLE void add(Application* application);
356+ Q_INVOKABLE void remove(Application* application);
357+ Q_INVOKABLE bool contains(Application* application) const;
358+ Q_INVOKABLE void clear();
359+
360 Q_SIGNALS:
361 void countChanged();
362
363 private:
364 Q_DISABLE_COPY(ApplicationListModel)
365
366- void add(Application* application);
367- void remove(Application* application);
368- bool contains(Application* application) const { return m_applications.contains(application); }
369+ static void appendApplication(QQmlListProperty<Application> *list,
370+ Application *application);
371+ static int countApplications(QQmlListProperty<Application> *list);
372+ static Application* atApplication(QQmlListProperty<Application> *list, int index);
373+ static void clearApplications(QQmlListProperty<Application> *list);
374
375 QHash<int,QByteArray> m_roleNames;
376 QList<Application*> m_applications;
377
378=== modified file 'tests/mocks/Ubuntu/Application/ApplicationManager.cpp'
379--- tests/mocks/Ubuntu/Application/ApplicationManager.cpp 2013-04-18 13:06:18 +0000
380+++ tests/mocks/Ubuntu/Application/ApplicationManager.cpp 2013-04-22 15:40:50 +0000
381@@ -120,6 +120,25 @@
382 return application;
383 }
384
385+void ApplicationManager::stopProcess(Application* application)
386+{
387+ if (m_mainStageApplications->contains(application)) {
388+ m_mainStageApplications->remove(application);
389+
390+ if (m_mainStageFocusedApplication == application) {
391+ m_mainStageFocusedApplication = 0;
392+ Q_EMIT mainStageFocusedApplicationChanged();
393+ }
394+ } else if (m_sideStageApplications->contains(application)){
395+ m_sideStageApplications->remove(application);
396+
397+ if (m_sideStageFocusedApplication == application) {
398+ m_sideStageFocusedApplication = 0;
399+ Q_EMIT sideStageFocusedApplicationChanged();
400+ }
401+ }
402+}
403+
404 void ApplicationManager::focusApplication(int handle)
405 {
406 for (int i = 0; i < m_mainStageApplications->m_applications.count(); ++i) {
407
408=== modified file 'tests/mocks/Ubuntu/Application/ApplicationManager.h'
409--- tests/mocks/Ubuntu/Application/ApplicationManager.h 2013-04-18 13:06:18 +0000
410+++ tests/mocks/Ubuntu/Application/ApplicationManager.h 2013-04-22 15:40:50 +0000
411@@ -85,7 +85,7 @@
412 Q_INVOKABLE Application* startProcess(QString desktopFile,
413 ExecFlags flags,
414 QStringList arguments = QStringList());
415- Q_INVOKABLE void stopProcess(Application* /*application*/) {}
416+ Q_INVOKABLE void stopProcess(Application* application);
417 Q_INVOKABLE void startWatcher() {}
418
419 Q_SIGNALS:
420
421=== modified file 'tests/mocks/Ubuntu/Application/plugin.cpp'
422--- tests/mocks/Ubuntu/Application/plugin.cpp 2013-04-12 14:41:56 +0000
423+++ tests/mocks/Ubuntu/Application/plugin.cpp 2013-04-22 15:40:50 +0000
424@@ -34,6 +34,5 @@
425 uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
426 qmlRegisterType<Application>(uri, 0, 1, "Application");
427 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
428- qmlRegisterUncreatableType<ApplicationListModel>(
429- uri, 0, 1, "ApplicationListModel", "ApplicationListModel can't be instantiated");
430+ qmlRegisterType<ApplicationListModel>(uri, 0, 1, "ApplicationListModel");
431 }
432
433=== modified file 'tests/qmltests/CMakeLists.txt'
434--- tests/qmltests/CMakeLists.txt 2013-04-22 09:57:23 +0000
435+++ tests/qmltests/CMakeLists.txt 2013-04-22 15:40:50 +0000
436@@ -44,6 +44,7 @@
437 add_qml_test(Dash DashPreview)
438 add_qml_test(Dash PeoplePreview)
439 add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins)
440+add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
441 add_qml_test(Greeter Greeter)
442 add_qml_test(Hud Hud)
443 add_qml_test(Launcher Launcher)
444
445=== added directory 'tests/qmltests/Dash/Apps'
446=== added file 'tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml'
447--- tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml 1970-01-01 00:00:00 +0000
448+++ tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml 2013-04-22 15:40:50 +0000
449@@ -0,0 +1,205 @@
450+/*
451+ * Copyright 2013 Canonical Ltd.
452+ *
453+ * This program is free software; you can redistribute it and/or modify
454+ * it under the terms of the GNU General Public License as published by
455+ * the Free Software Foundation; version 3.
456+ *
457+ * This program is distributed in the hope that it will be useful,
458+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
459+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
460+ * GNU General Public License for more details.
461+ *
462+ * You should have received a copy of the GNU General Public License
463+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
464+ */
465+
466+import QtQuick 2.0
467+import QtTest 1.0
468+import Ubuntu.Application 0.1
469+import "../../../../Dash/Apps"
470+import "../../../../Applications/applications.js" as ApplicationsModel
471+import Unity.Test 0.1 as UT
472+
473+Item {
474+ width: units.gu(50)
475+ height: units.gu(40)
476+
477+ QtObject {
478+ id: fakeApplicationManager
479+
480+ property bool sideStageEnabled: false
481+
482+ function stopProcess(application) {
483+ fakeRunningAppsModel.remove(application)
484+ }
485+ }
486+
487+ QtObject {
488+ id: shell
489+ property bool dashShown: true
490+ property bool stageScreenshotsReady: false
491+ property var applicationManager: fakeApplicationManager
492+
493+ function activateApplication(desktopFile) {
494+ }
495+ }
496+
497+ ApplicationListModel { id: fakeRunningAppsModel }
498+
499+ Application {
500+ id: phoneApp
501+ name: "Phone"
502+ icon: "phone-app"
503+ exec: "/usr/bin/phone-app"
504+ stage: ApplicationsModel.MainStage
505+ desktopFile: "phone.desktop"
506+ imageQml: "import QtQuick 2.0\n" +
507+ "Rectangle { \n" +
508+ " anchors.fill:parent \n" +
509+ " color:'darkgreen' \n" +
510+ " Text { anchors.centerIn: parent; text: 'PHONE' } \n" +
511+ "}"
512+ }
513+
514+ Application {
515+ id: calendarApp
516+ name: "Calendar"
517+ icon: "calendar-app"
518+ exec: "/usr/bin/calendar-app"
519+ stage: ApplicationsModel.MainStage
520+ desktopFile: "calendar.desktop"
521+ imageQml: "import QtQuick 2.0\n" +
522+ "Rectangle { \n" +
523+ " anchors.fill:parent \n" +
524+ " color:'darkblue' \n" +
525+ " Text { anchors.centerIn: parent; text: 'CALENDAR'\n" +
526+ " color:'white'} \n" +
527+ "}"
528+ }
529+
530+ function resetRunningApplications() {
531+ fakeRunningAppsModel.clear()
532+ fakeRunningAppsModel.add(phoneApp)
533+ fakeRunningAppsModel.add(calendarApp)
534+ }
535+
536+ Component.onCompleted: {
537+ resetRunningApplications()
538+ }
539+
540+ // The component under test
541+ RunningApplicationsGrid {
542+ id: runningApplicationsGrid
543+ anchors.fill: parent
544+ firstModel: fakeRunningAppsModel
545+ }
546+
547+ UT.UnityTestCase {
548+ name: "RunningApplicationsGrid"
549+ when: windowShown
550+
551+ function init() {
552+ runningApplicationsGrid.terminationModeEnabled = false
553+ resetRunningApplications()
554+ }
555+
556+ property var calendarTile
557+ property var phoneTile
558+
559+ property var isCalendarLongPressed: false
560+ function onCalendarLongPressed() {isCalendarLongPressed = true}
561+
562+ property var isPhoneLongPressed: false
563+ function onPhoneLongPressed() {isPhoneLongPressed = true}
564+
565+ // Tiles should go to termination mode when any one of them is long-pressed.
566+ // Long-pressing when they're in termination mode brings them back to activation mode
567+ function test_enterTerminationMode() {
568+ calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
569+ verify(calendarTile != undefined)
570+ calendarTile.onPressAndHold.connect(onCalendarLongPressed)
571+
572+ phoneTile = findChild(runningApplicationsGrid, "runningAppTile Phone")
573+ verify(phoneTile != undefined)
574+ phoneTile.onPressAndHold.connect(onPhoneLongPressed)
575+
576+ compare(calendarTile.terminationModeEnabled, false)
577+ compare(phoneTile.terminationModeEnabled, false)
578+ compare(runningApplicationsGrid.terminationModeEnabled, false)
579+
580+ isCalendarLongPressed = false
581+ mousePress(calendarTile, calendarTile.width/2, calendarTile.height/2)
582+ tryCompareFunction(checkSwitchToTerminationModeAfterLongPress, true)
583+
584+ mouseRelease(calendarTile, calendarTile.width/2, calendarTile.height/2)
585+
586+ compare(calendarTile.terminationModeEnabled, true)
587+ compare(phoneTile.terminationModeEnabled, true)
588+ compare(runningApplicationsGrid.terminationModeEnabled, true)
589+
590+ isPhoneLongPressed = false
591+ mousePress(phoneTile, phoneTile.width/2, phoneTile.height/2)
592+ tryCompareFunction(checkSwitchToActivationModeAfterLongPress, true)
593+
594+ mouseRelease(phoneTile, phoneTile.width/2, phoneTile.height/2)
595+
596+ compare(calendarTile.terminationModeEnabled, false)
597+ compare(phoneTile.terminationModeEnabled, false)
598+ compare(runningApplicationsGrid.terminationModeEnabled, false)
599+
600+ calendarTile.onPressAndHold.disconnect(onCalendarLongPressed)
601+ phoneTile.onPressAndHold.disconnect(onPhoneLongPressed)
602+ }
603+
604+ // Checks that components swicth to termination mode after (and only after) a long
605+ // press happens on Calendar tile.
606+ function checkSwitchToTerminationModeAfterLongPress() {
607+ compare(calendarTile.terminationModeEnabled, isCalendarLongPressed)
608+ compare(phoneTile.terminationModeEnabled, isCalendarLongPressed)
609+ compare(runningApplicationsGrid.terminationModeEnabled, isCalendarLongPressed)
610+
611+ return isCalendarLongPressed &&
612+ calendarTile.terminationModeEnabled &&
613+ phoneTile.terminationModeEnabled &&
614+ runningApplicationsGrid.terminationModeEnabled
615+ }
616+
617+ // Checks that components swicth to activation mode after (and only after) a long
618+ // press happens on Phone tile.
619+ function checkSwitchToActivationModeAfterLongPress() {
620+ compare(calendarTile.terminationModeEnabled, !isPhoneLongPressed)
621+ compare(phoneTile.terminationModeEnabled, !isPhoneLongPressed)
622+ compare(runningApplicationsGrid.terminationModeEnabled, !isPhoneLongPressed)
623+
624+ return isPhoneLongPressed &&
625+ !calendarTile.terminationModeEnabled &&
626+ !phoneTile.terminationModeEnabled &&
627+ !runningApplicationsGrid.terminationModeEnabled
628+ }
629+
630+ // While on termination mode, clicking a running application tile causes the
631+ // corresponding application to be terminated.
632+ function test_clickTileToTerminateApp() {
633+ runningApplicationsGrid.terminationModeEnabled = true
634+
635+ var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
636+ verify(calendarTile != undefined)
637+
638+ verify(fakeRunningAppsModel.contains(calendarApp))
639+
640+ mouseClick(calendarTile, calendarTile.width/2, calendarTile.height/2)
641+
642+ verify(!fakeRunningAppsModel.contains(calendarApp))
643+
644+ // The tile for the Calendar app should eventually vanish since the
645+ // application has been terminated
646+ tryCompareFunction(checkCalendarTileExists, false)
647+ }
648+
649+ function checkCalendarTileExists() {
650+ return findChild(runningApplicationsGrid, "runningAppTile Calendar")
651+ != undefined
652+ }
653+ }
654+}

Subscribers

People subscribed via source and target branches