Merge lp:~mzanetti/unity8/dash-as-app into lp:unity8

Proposed by Michael Zanetti
Status: Rejected
Rejected by: Michael Zanetti
Proposed branch: lp:~mzanetti/unity8/dash-as-app
Merge into: lp:unity8
Prerequisite: lp:~mzanetti/unity8/drop-running-apps-from-dash
Diff against target: 2874 lines (+972/-763) (has conflicts)
51 files modified
CMakeLists.txt (+1/-0)
data/CMakeLists.txt (+5/-2)
data/unity8-dash.conf (+22/-0)
data/unity8-dash.desktop.in (+9/-0)
data/unity8.conf (+1/-7)
debian/unity8.install (+3/-0)
plugins/Unity/CMakeLists.txt (+1/-0)
plugins/Unity/DashCommunicator/CMakeLists.txt (+19/-0)
plugins/Unity/DashCommunicator/dashcommunicator.cpp (+43/-0)
plugins/Unity/DashCommunicator/dashcommunicator.h (+35/-0)
plugins/Unity/DashCommunicator/dashcommunicatorservice.cpp (+30/-0)
plugins/Unity/DashCommunicator/dashcommunicatorservice.h (+38/-0)
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp (+39/-0)
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h (+39/-0)
plugins/Unity/DashCommunicator/plugin.cpp (+31/-0)
plugins/Unity/DashCommunicator/plugin.h (+34/-0)
plugins/Unity/DashCommunicator/qmldir (+3/-0)
plugins/Unity/Launcher/launchermodel.cpp (+5/-0)
qml/Components/EdgeDemo.qml (+5/-6)
qml/Dash/Dash.qml (+8/-0)
qml/Dash/DashApplication.qml (+13/-0)
qml/Dash/GenericScopeView.qml (+1/-1)
qml/Dash/ScopeListView.qml (+0/-5)
qml/Launcher/Launcher.qml (+2/-3)
qml/Shell.qml (+23/-124)
qml/Stages/PhoneStage.qml (+60/-26)
qml/Stages/SpreadDelegate.qml (+6/-0)
qml/Stages/TabletStage.qml (+82/-36)
qml/Stages/TransformedSpreadDelegate.qml (+6/-5)
qml/Stages/TransformedTabletSpreadDelegate.qml (+29/-1)
src/CMakeLists.txt (+1/-0)
src/Dash/CMakeLists.txt (+12/-0)
src/Dash/main.cpp (+49/-0)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+0/-62)
tests/mocks/Unity/Application/ApplicationInfo.h (+0/-10)
tests/mocks/Unity/Application/ApplicationManager.cpp (+15/-142)
tests/mocks/Unity/Application/ApplicationManager.h (+0/-16)
tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp (+1/-1)
tests/mocks/Unity/CMakeLists.txt (+1/-0)
tests/mocks/Unity/DashCommunicator/CMakeLists.txt (+18/-0)
tests/mocks/Unity/DashCommunicator/dashcommunicator.cpp (+37/-0)
tests/mocks/Unity/DashCommunicator/dashcommunicator.h (+39/-0)
tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cpp (+33/-0)
tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.h (+36/-0)
tests/mocks/Unity/DashCommunicator/plugin.cpp (+31/-0)
tests/mocks/Unity/DashCommunicator/plugin.h (+34/-0)
tests/mocks/Unity/DashCommunicator/qmldir (+3/-0)
tests/qmltests/Dash/tst_Dash.qml (+19/-0)
tests/qmltests/Stages/tst_PhoneStage.qml (+3/-2)
tests/qmltests/tst_Shell.qml (+43/-277)
tests/qmltests/tst_ShellWithPin.qml (+4/-37)
Text conflict in debian/changelog
To merge this branch: bzr merge lp:~mzanetti/unity8/dash-as-app
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Michael Zanetti (community) Disapprove
Daniel d'Andrada (community) Needs Fixing
Michał Sawicz Needs Fixing
Review via email: mp+227868@code.launchpad.net

Commit message

Split the dash from the shell into a separate app

Description of the change

### Notes for reviewers ###

* It depends on QtCompositor (silo 6). Alternatively this and qtcomp rc can be installed through ppa:unity-team/phone-right-edge

* Adds an upstart script to autolaunch and respawn the dash app.

* Introduces a new plugin DashCommunicator which consists of a server and a client part, to be used to communicate between shell and dash.

* Adds a mock plugin for the DashCommunicator plugin

* Drops a whole bunch of useless stuff from MockApplicationManager which isn't needed any more since QtCompositor. (The fake placing of surfaces behind the shell - as unity-mir did it.). This could/should have been gone earlier, but only became a visible issue with this branch. Those changes are isolated in commit 1099.

* Adds a fake unity8-dash app (screenshot) to MockApplicationManager which will always be there, simulating the running dash app.

* Drops some code and tests for stages in/out dragging, given the dash is a regular app now, the stages are always visible now.

* Updates/simplifies shell and dash tests for the split app scenario.

* Does some slight updates to the Stages to keep the Dash behind the current app when not in spread mode so a left edge flick still shows the dash behind the app.

### Still TODO ###

* Make autopilot work again

### Checklist ###

 * Are there any related MPs required for this MP to build/function as expected? Please list.

https://code.launchpad.net/~unity-team/unity-scopes-shell/drop-appid-workaround/+merge/228427

 * Did you perform an exploratory manual test run of your code change and any related functionality?

Yes, works fine for me, except launching apps from dash and AP (see TODO section)

 * Did you make sure that your branch does not contain spurious tags?

Yes.

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

I added the additional binary and upstart script to installs, to be reviewed.

 * If you changed the UI, has there been a design review?

Vesa is on holiday atm. I tried to keep UI changes as minimal as possible to fulfill the design request of adding the dash into the spread. So should be good I hope.

To post a comment you must log in.
Revision history for this message
Michał Sawicz (saviq) wrote :

See inline.

Now, on DashCommunicator... I was wondering if we should instead register a scope:/// url handler, and then just dispatch scope:///clickscope/ when needed.

Only tricky part would be on left edge, when we should bring the dash in from the left with your finger, url-dispatcher would request focus, but we should ignore that request then.

review: Needs Fixing
lp:~mzanetti/unity8/dash-as-app updated
1123. By Michael Zanetti

review-fixes for upstart script

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> See inline.

Fixed/replied.

>
> Now, on DashCommunicator... I was wondering if we should instead register a
> scope:/// url handler, and then just dispatch scope:///clickscope/ when
> needed.
>
> Only tricky part would be on left edge, when we should bring the dash in from
> the left with your finger, url-dispatcher would request focus, but we should
> ignore that request then.

I agree the url handler sounds like a useful/required thing for the future, however, as you already mentioned it adds complexity. I tend to think we need both in the long run.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

inline...

lp:~mzanetti/unity8/dash-as-app updated
1124. By Michael Zanetti

merge prereq

1125. By Michael Zanetti

merge prereq

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

Repeating as the inline comments don't work so well.

In SpreadDelegate.qml

"""
1114 + objectName: "surfaceContainer" + index
"""

Using index from inside SpreadDelegate breaks encapsulation. You can achive the same result in tst_Shell.qml by fetching first "appDelegate0" and then fecthing the "surfaceContainer" from within "appDelegate0".

lp:~mzanetti/unity8/dash-as-app updated
1126. By Michael Zanetti

fix review comment and test

Revision history for this message
Michael Zanetti (mzanetti) wrote :

thanks Daniel. Fixed it.

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

Solved the issue I found. But as I didn't actually go through the full diff at all I'll just unblock it by abstaining.

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

$ make tryShell
1 - launch an app from the launcher
2 - tap on it
<vkb shows up>
3 - do a left edge drag

expected result:
vkb should go away as soon as the app window starts moving righwards.
Just like when you start a right-edge gesture.

actual result:
vkb goes away only once you finish the gesture

vkb going away means the MirSurfaceItem losing focus which on its turn means the SpreadDelegate.interactive going false.

review: Needs Fixing
lp:~mzanetti/unity8/dash-as-app updated
1127. By Michael Zanetti

make the spread non-interactive when dragging from left

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> $ make tryShell
> 1 - launch an app from the launcher
> 2 - tap on it
> <vkb shows up>
> 3 - do a left edge drag
>
> expected result:
> vkb should go away as soon as the app window starts moving righwards.
> Just like when you start a right-edge gesture.
>
> actual result:
> vkb goes away only once you finish the gesture
>
> vkb going away means the MirSurfaceItem losing focus which on its turn means
> the SpreadDelegate.interactive going false.

another good catch. fixed.

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

Unmerged revisions

1127. By Michael Zanetti

make the spread non-interactive when dragging from left

1126. By Michael Zanetti

fix review comment and test

1125. By Michael Zanetti

merge prereq

1124. By Michael Zanetti

merge prereq

1123. By Michael Zanetti

review-fixes for upstart script

1122. By Michael Zanetti

one more fix for the side stage

1121. By Michael Zanetti

more fixes for tablet mode

1120. By Michael Zanetti

small fix for tablet stage

1119. By Michael Zanetti

fix some visual glitches in the tabletstage

1118. By Michael Zanetti

don't bahave xTranslate when dragging the side stage

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-06-11 15:36:51 +0000
+++ CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -96,6 +96,7 @@
96declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)96declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
9797
98set(SHELL_APP unity8)98set(SHELL_APP unity8)
99set(DASH_APP unity8-dash)
99set(SCOPE_TOOL unity-scope-tool)100set(SCOPE_TOOL unity-scope-tool)
100101
101include_directories(102include_directories(
102103
=== modified file 'data/CMakeLists.txt'
--- data/CMakeLists.txt 2014-06-11 15:36:51 +0000
+++ data/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -1,7 +1,10 @@
1# generate desktop file1# generate desktop files
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3configure_file(${DASH_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop @ONLY)
34
4# install desktop files5# install desktop files
5install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop6install(FILES
7 ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
8 ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop
6 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications9 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
7 )10 )
811
=== added file 'data/unity8-dash.conf'
--- data/unity8-dash.conf 1970-01-01 00:00:00 +0000
+++ data/unity8-dash.conf 2014-07-28 15:50:22 +0000
@@ -0,0 +1,22 @@
1description "Unity Shell v8 Dash"
2author "Michael Zanetti <michael.zanetti@canonical.com>"
3
4emits scope-ui-starting
5
6start on started unity8
7stop on stopping unity8
8
9kill timeout 120
10
11respawn
12
13pre-start script
14 if [ -z "$UNITY_SCOPES_LIST" ]; then
15 # FIXME: remove once we have this in dconf
16 initctl set-env UNITY_SCOPES_LIST="scopes;clickscope;musicaggregator;videoaggregator"
17 fi
18
19 initctl emit scope-ui-starting
20end script
21
22exec ${BINARY:-unity8-dash} $ARGS --desktop_file_hint=/usr/share/applications/unity8-dash.desktop
023
=== added file 'data/unity8-dash.desktop.in'
--- data/unity8-dash.desktop.in 1970-01-01 00:00:00 +0000
+++ data/unity8-dash.desktop.in 2014-07-28 15:50:22 +0000
@@ -0,0 +1,9 @@
1[Desktop Entry]
2Type=Application
3Name=Unity 8 Dash
4Comment=The converged Unity shell dash
5Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-dash
6Terminal=false
7Icon=unity8
8NoDisplay=true
9X-Ubuntu-Touch=true
010
=== modified file 'data/unity8.conf'
--- data/unity8.conf 2014-07-16 15:51:56 +0000
+++ data/unity8.conf 2014-07-28 15:50:22 +0000
@@ -1,7 +1,7 @@
1description "Unity Shell v8"1description "Unity Shell v8"
2author "Ricardo Mendoza <ricmm@ubuntu.com>"2author "Ricardo Mendoza <ricmm@ubuntu.com>"
33
4emits scope-ui-starting indicator-services-start4emits indicator-services-start
55
6start on ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger)) and started dbus6start on ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger)) and started dbus
7stop on desktop-end7stop on desktop-end
@@ -34,11 +34,6 @@
34 gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"34 gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"
35 fi35 fi
3636
37 if [ -z "$UNITY_SCOPES_LIST" ]; then
38 # FIXME: remove once we have this in dconf
39 initctl set-env UNITY_SCOPES_LIST="scopes;clickscope;musicaggregator;videoaggregator"
40 fi
41
42 # Remove the socket if still there37 # Remove the socket if still there
43 if [ -S "$MIR_SERVER_FILE" ]; then38 if [ -S "$MIR_SERVER_FILE" ]; then
44 rm "$MIR_SERVER_FILE"39 rm "$MIR_SERVER_FILE"
@@ -46,7 +41,6 @@
4641
47 initctl set-env --global MIR_SERVER_PROMPT_FILE=142 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
4843
49 initctl emit scope-ui-starting
50 initctl emit --no-wait indicator-services-start44 initctl emit --no-wait indicator-services-start
51end script45end script
5246
5347
=== modified file 'debian/unity8.install'
--- debian/unity8.install 2014-06-11 15:36:51 +0000
+++ debian/unity8.install 2014-07-28 15:50:22 +0000
@@ -1,6 +1,9 @@
1data/unity8.conf usr/share/upstart/sessions/1data/unity8.conf usr/share/upstart/sessions/
2data/unity8-dash.conf usr/share/upstart/sessions/
2usr/bin/unity83usr/bin/unity8
4usr/bin/unity8-dash
3usr/share/applications/unity8.desktop5usr/share/applications/unity8.desktop
6usr/share/applications/unity8-dash.desktop
4usr/share/unity8/Greeter7usr/share/unity8/Greeter
5usr/share/unity8/Hud8usr/share/unity8/Hud
6usr/share/unity8/Launcher9usr/share/unity8/Launcher
710
=== modified file 'plugins/Unity/CMakeLists.txt'
--- plugins/Unity/CMakeLists.txt 2014-05-06 14:28:48 +0000
+++ plugins/Unity/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -1,3 +1,4 @@
1add_subdirectory(Indicators)1add_subdirectory(Indicators)
2add_subdirectory(Launcher)2add_subdirectory(Launcher)
3add_subdirectory(Session)3add_subdirectory(Session)
4add_subdirectory(DashCommunicator)
45
=== added directory 'plugins/Unity/DashCommunicator'
=== added file 'plugins/Unity/DashCommunicator/CMakeLists.txt'
--- plugins/Unity/DashCommunicator/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -0,0 +1,19 @@
1include_directories(
2 ${CMAKE_CURRENT_SOURCE_DIR}
3)
4
5set(QMLDASHCOMMUNICATORPLUGIN_SRC
6 plugin.cpp
7 dbusdashcommunicatorservice.cpp
8 dashcommunicatorservice.cpp
9 dashcommunicator.cpp
10 )
11
12add_library(DashCommunicator-qml MODULE
13 ${QMLDASHCOMMUNICATORPLUGIN_SRC}
14 )
15
16qt5_use_modules(DashCommunicator-qml DBus Qml)
17
18# export the qmldir and qmltypes files
19add_unity8_plugin(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS DashCommunicator-qml)
020
=== added file 'plugins/Unity/DashCommunicator/dashcommunicator.cpp'
--- plugins/Unity/DashCommunicator/dashcommunicator.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/dashcommunicator.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "dashcommunicator.h"
18
19#include <QObject>
20#include <QDBusConnection>
21#include <QDBusInterface>
22#include <QDebug>
23
24DashCommunicator::DashCommunicator(QObject *parent):
25 QObject(parent)
26{
27}
28
29DashCommunicator::~DashCommunicator()
30{
31}
32
33
34void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool reset)
35{
36 QDBusConnection connection = QDBusConnection::sessionBus();
37 QDBusInterface dashIface ("com.canonical.UnityDash",
38 "/com/canonical/UnityDash",
39 "",
40 connection);
41
42 dashIface.call("SetCurrentScope", scopeId, animate, reset);
43}
044
=== added file 'plugins/Unity/DashCommunicator/dashcommunicator.h'
--- plugins/Unity/DashCommunicator/dashcommunicator.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/dashcommunicator.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DASHCOMMUNICATOR_H
18#define DASHCOMMUNICATOR_H
19
20#include <QObject>
21
22class DashCommunicator: public QObject
23{
24 Q_OBJECT
25 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26
27public:
28 DashCommunicator(QObject *parent = 0);
29 ~DashCommunicator();
30
31public Q_SLOTS:
32 void setCurrentScope(const QString &scopeId, bool animate, bool reset);
33};
34
35#endif
036
=== added file 'plugins/Unity/DashCommunicator/dashcommunicatorservice.cpp'
--- plugins/Unity/DashCommunicator/dashcommunicatorservice.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/dashcommunicatorservice.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,30 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "dashcommunicatorservice.h"
18
19DashCommunicatorService::DashCommunicatorService(QObject *parent):
20 QObject(parent),
21 m_dbusService(new DBusDashCommunicatorService(this))
22{
23 connect(m_dbusService, &DBusDashCommunicatorService::setCurrentScope, this, &DashCommunicatorService::setCurrentScope);
24}
25
26
27DashCommunicatorService::~DashCommunicatorService()
28{
29
30}
031
=== added file 'plugins/Unity/DashCommunicator/dashcommunicatorservice.h'
--- plugins/Unity/DashCommunicator/dashcommunicatorservice.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/dashcommunicatorservice.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DASHCOMMUNICATORSERVICE_H
18#define DASHCOMMUNICATORSERVICE_H
19
20#include "dbusdashcommunicatorservice.h"
21
22#include <QObject>
23
24class DashCommunicatorService: public QObject
25{
26 Q_OBJECT
27public:
28 DashCommunicatorService(QObject *parent = 0);
29 ~DashCommunicatorService();
30
31Q_SIGNALS:
32 void setCurrentScope(const QString &scopeId, bool animate, bool reset);
33
34private:
35 DBusDashCommunicatorService *m_dbusService;
36};
37
38#endif // DBUSUNITYSESSIONSERVICE_H
039
=== added file 'plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp'
--- plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "dbusdashcommunicatorservice.h"
18
19#include <QDBusConnection>
20#include <QDBusInterface>
21#include <QDebug>
22
23DBusDashCommunicatorService::DBusDashCommunicatorService(QObject *parent):
24 QObject(parent)
25{
26 QDBusConnection connection = QDBusConnection::sessionBus();
27
28 connection.registerService("com.canonical.UnityDash");
29 connection.registerObject("/com/canonical/UnityDash", this, QDBusConnection::ExportScriptableSlots);
30}
31
32DBusDashCommunicatorService::~DBusDashCommunicatorService()
33{
34}
35
36void DBusDashCommunicatorService::SetCurrentScope(const QString &scopeId, bool animate, bool reset)
37{
38 Q_EMIT setCurrentScope(scopeId, animate, reset);
39}
040
=== added file 'plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h'
--- plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DBUSDASHCOMMUNICATORSERVICE_H
18#define DBUSDASHCOMMUNICATORSERVICE_H
19
20#include <QObject>
21
22class DBusDashCommunicatorService: public QObject
23{
24 Q_OBJECT
25 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26
27public:
28 DBusDashCommunicatorService(QObject *parent = 0);
29 ~DBusDashCommunicatorService();
30
31Q_SIGNALS:
32 void setCurrentScope(const QString &scopeId, bool animate, bool reset);
33
34public Q_SLOTS:
35 Q_SCRIPTABLE void SetCurrentScope(const QString &scopeId, bool animate, bool reset);
36
37};
38
39#endif // DBUSUNITYSESSIONSERVICE_H
040
=== added file 'plugins/Unity/DashCommunicator/plugin.cpp'
--- plugins/Unity/DashCommunicator/plugin.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/plugin.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,31 @@
1/*
2 * Copyright (C) 2014 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 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */
18
19#include "plugin.h"
20#include "dashcommunicator.h"
21#include "dashcommunicatorservice.h"
22
23#include <QDBusConnection>
24#include <QtQml/qqml.h>
25
26void DashCommunicatorPlugin::registerTypes(const char *uri)
27{
28 Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
29 qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
30 qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
31}
032
=== added file 'plugins/Unity/DashCommunicator/plugin.h'
--- plugins/Unity/DashCommunicator/plugin.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/plugin.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2014 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 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */
18
19#ifndef DASHCOMMUNICATOR_PLUGIN_H
20#define DASHCOMMUNICATOR_PLUGIN_H
21
22#include <QtQml/QQmlEngine>
23#include <QtQml/QQmlExtensionPlugin>
24
25class DashCommunicatorPlugin : public QQmlExtensionPlugin
26{
27 Q_OBJECT
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
30public:
31 void registerTypes(const char *uri);
32};
33
34#endif
035
=== added file 'plugins/Unity/DashCommunicator/qmldir'
--- plugins/Unity/DashCommunicator/qmldir 1970-01-01 00:00:00 +0000
+++ plugins/Unity/DashCommunicator/qmldir 2014-07-28 15:50:22 +0000
@@ -0,0 +1,3 @@
1module Unity.DashCommunicator
2plugin DashCommunicator-qml
3typeinfo DashCommunicator.qmltypes
04
=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
--- plugins/Unity/Launcher/launchermodel.cpp 2014-07-24 06:11:07 +0000
+++ plugins/Unity/Launcher/launchermodel.cpp 2014-07-28 15:50:22 +0000
@@ -317,6 +317,11 @@
317 return;317 return;
318 }318 }
319319
320 if (app->appId() == "unity8-dash") {
321 // Not adding the dash app
322 return;
323 }
324
320 bool found = false;325 bool found = false;
321 Q_FOREACH(LauncherItem *item, m_list) {326 Q_FOREACH(LauncherItem *item, m_list) {
322 if (app->appId() == item->appId()) {327 if (app->appId() == item->appId()) {
323328
=== modified file 'qml/Components/EdgeDemo.qml'
--- qml/Components/EdgeDemo.qml 2014-06-11 15:36:51 +0000
+++ qml/Components/EdgeDemo.qml 2014-07-28 15:50:22 +0000
@@ -22,7 +22,6 @@
22 id: demo22 id: demo
2323
24 property Item greeter24 property Item greeter
25 property Item dash
26 property Item launcher25 property Item launcher
27 property Item indicators26 property Item indicators
28 property Item underlay27 property Item underlay
@@ -144,7 +143,7 @@
144 "edge": "top",143 "edge": "top",
145 "title": i18n.tr("Top edge"),144 "title": i18n.tr("Top edge"),
146 "text": i18n.tr("Try swiping from the top edge to access the indicators"),145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
147 "anchors.fill": demo.dash,146 "anchors.fill": demo.underlay,
148 });147 });
149 }148 }
150 if (d.topEdgeDemo) {149 if (d.topEdgeDemo) {
@@ -193,12 +192,12 @@
193 function startLeftEdgeDemo() {192 function startLeftEdgeDemo() {
194 demo.panelEnabled = false;193 demo.panelEnabled = false;
195 demo.launcherEnabled = true;194 demo.launcherEnabled = true;
196 if (demo.dash && demo.underlay) {195 if (demo.underlay) {
197 d.leftEdgeDemo = d.overlay.createObject(demo.underlay, {196 d.leftEdgeDemo = d.overlay.createObject(demo.underlay, {
198 "edge": "left",197 "edge": "left",
199 "title": i18n.tr("Left edge"),198 "title": i18n.tr("Left edge"),
200 "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),199 "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
201 "anchors.fill": demo.dash,200 "anchors.fill": demo.underlay,
202 });201 });
203 }202 }
204 if (d.leftEdgeDemo) {203 if (d.leftEdgeDemo) {
@@ -221,12 +220,12 @@
221220
222 function startFinalEdgeDemo() {221 function startFinalEdgeDemo() {
223 demo.launcherEnabled = false;222 demo.launcherEnabled = false;
224 if (demo.dash && demo.underlay) {223 if (demo.underlay) {
225 d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {224 d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
226 "edge": "none",225 "edge": "none",
227 "title": i18n.tr("Well done"),226 "title": i18n.tr("Well done"),
228 "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap on the screen to start"),227 "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap on the screen to start"),
229 "anchors.fill": demo.dash,228 "anchors.fill": demo.underlay,
230 "showSkip": false,229 "showSkip": false,
231 });230 });
232 }231 }
233232
=== modified file 'qml/Dash/Dash.qml'
--- qml/Dash/Dash.qml 2014-07-07 08:20:04 +0000
+++ qml/Dash/Dash.qml 2014-07-28 15:50:22 +0000
@@ -18,6 +18,7 @@
18import Ubuntu.Components 0.118import Ubuntu.Components 0.1
19import Unity 0.219import Unity 0.2
20import Utils 0.120import Utils 0.1
21import Unity.DashCommunicator 0.1
21import "../Components"22import "../Components"
2223
23Showable {24Showable {
@@ -29,6 +30,13 @@
29 property string showScopeOnLoaded: "clickscope"30 property string showScopeOnLoaded: "clickscope"
30 property real contentScale: 1.031 property real contentScale: 1.0
3132
33 DashCommunicatorService {
34 objectName: "dashCommunicatorService"
35 onSetCurrentScope: {
36 dash.setCurrentScope(scopeId, animate, reset)
37 }
38 }
39
32 function setCurrentScope(scopeId, animate, reset) {40 function setCurrentScope(scopeId, animate, reset) {
33 var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)41 var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)
3442
3543
=== added file 'qml/Dash/DashApplication.qml'
--- qml/Dash/DashApplication.qml 1970-01-01 00:00:00 +0000
+++ qml/Dash/DashApplication.qml 2014-07-28 15:50:22 +0000
@@ -0,0 +1,13 @@
1import QtQuick 2.2
2import Ubuntu.Components 1.0
3
4MainView {
5 width: units.gu(40)
6 height: units.gu(71)
7
8 useDeprecatedToolbar: false
9
10 Dash {
11 anchors.fill: parent
12 }
13}
014
=== modified file 'qml/Dash/GenericScopeView.qml'
--- qml/Dash/GenericScopeView.qml 2014-07-28 15:50:19 +0000
+++ qml/Dash/GenericScopeView.qml 2014-07-28 15:50:22 +0000
@@ -46,7 +46,7 @@
46 }46 }
4747
48 function activateApp(appId) {48 function activateApp(appId) {
49 shell.activateApplication(appId);49 Qt.openUrlExternally(appId);
50 }50 }
5151
52 function positionAtBeginning() {52 function positionAtBeginning() {
5353
=== modified file 'qml/Dash/ScopeListView.qml'
--- qml/Dash/ScopeListView.qml 2014-06-11 15:36:51 +0000
+++ qml/Dash/ScopeListView.qml 2014-07-28 15:50:22 +0000
@@ -20,9 +20,4 @@
20ListViewWithPageHeader {20ListViewWithPageHeader {
21 maximumFlickVelocity: height * 1021 maximumFlickVelocity: height * 10
22 flickDeceleration: height * 222 flickDeceleration: height * 2
23
24 Connections {
25 target: greeter
26 onShownChanged: if (greeter.shown) showHeader()
27 }
28}23}
2924
=== added file 'qml/Dash/graphics/phone/screenshots/dash.png'
=== added file 'qml/Dash/graphics/phone/screenshots/unity8-dash@12.png'
30Binary files qml/Dash/graphics/phone/screenshots/unity8-dash@12.png 1970-01-01 00:00:00 +0000 and qml/Dash/graphics/phone/screenshots/unity8-dash@12.png 2014-07-28 15:50:22 +0000 differ25Binary files qml/Dash/graphics/phone/screenshots/unity8-dash@12.png 1970-01-01 00:00:00 +0000 and qml/Dash/graphics/phone/screenshots/unity8-dash@12.png 2014-07-28 15:50:22 +0000 differ
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2014-07-28 15:50:19 +0000
+++ qml/Launcher/Launcher.qml 2014-07-28 15:50:22 +0000
@@ -29,8 +29,7 @@
29 property int dragAreaWidth: units.gu(1)29 property int dragAreaWidth: units.gu(1)
30 property int minimizeDistance: units.gu(26)30 property int minimizeDistance: units.gu(26)
31 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?31 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
32 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) :32 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
33 (dragArea.dragging ? 0.001 : 0)
3433
35 readonly property bool shown: panel.x > -panel.width34 readonly property bool shown: panel.x > -panel.width
3635
@@ -245,7 +244,7 @@
245 if (!dragging) {244 if (!dragging) {
246 if (distance > panel.width / 2) {245 if (distance > panel.width / 2) {
247 if (distance > minimizeDistance) {246 if (distance > minimizeDistance) {
248 root.dash()247 root.fadeOut();
249 } else {248 } else {
250 root.switchToNextState("visible")249 root.switchToNextState("visible")
251 }250 }
252251
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2014-07-28 15:50:19 +0000
+++ qml/Shell.qml 2014-07-28 15:50:22 +0000
@@ -25,7 +25,6 @@
25import LightDM 0.1 as LightDM25import LightDM 0.1 as LightDM
26import Powerd 0.126import Powerd 0.1
27import SessionBroadcast 0.127import SessionBroadcast 0.1
28import "Dash"
29import "Greeter"28import "Greeter"
30import "Launcher"29import "Launcher"
31import "Panel"30import "Panel"
@@ -34,6 +33,7 @@
34import "Stages"33import "Stages"
35import Unity.Notifications 1.0 as NotificationBackend34import Unity.Notifications 1.0 as NotificationBackend
36import Unity.Session 0.135import Unity.Session 0.1
36import Unity.DashCommunicator 0.1
3737
38Item {38Item {
39 id: shell39 id: shell
@@ -48,8 +48,6 @@
48 property url background48 property url background
49 readonly property real panelHeight: panel.panelHeight49 readonly property real panelHeight: panel.panelHeight
5050
51 property bool dashShown: dash.shown && dash.available && underlay.visible
52
53 property bool sideStageEnabled: shell.width >= units.gu(100)51 property bool sideStageEnabled: shell.width >= units.gu(100)
54 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId52 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
5553
@@ -59,7 +57,6 @@
59 } else {57 } else {
60 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;58 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
61 ApplicationManager.startApplication(appId, execFlags);59 ApplicationManager.startApplication(appId, execFlags);
62 stages.show();
63 }60 }
64 }61 }
6562
@@ -86,6 +83,11 @@
86 id: volumeControl83 id: volumeControl
87 }84 }
8885
86 DashCommunicator {
87 id: dash
88 objectName: "dashCommunicator"
89 }
90
89 WindowKeysFilter {91 WindowKeysFilter {
90 // Handle but do not filter out volume keys92 // Handle but do not filter out volume keys
91 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }93 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
@@ -121,64 +123,12 @@
121 visible: !fullyCovered123 visible: !fullyCovered
122124
123 Image {125 Image {
124 anchors.fill: dash126 anchors.fill: underlay
125 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"127 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
126 fillMode: Image.PreserveAspectCrop128 fillMode: Image.PreserveAspectCrop
127 horizontalAlignment: Image.AlignRight129 horizontalAlignment: Image.AlignRight
128 verticalAlignment: Image.AlignTop130 verticalAlignment: Image.AlignTop
129 }131 }
130
131 Dash {
132 id: dash
133 objectName: "dash"
134
135 available: !LightDM.Greeter.active
136 hides: [stages, launcher, panel.indicators]
137 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0 &&
138 !(panel.indicators.fullyOpened && !sideStageEnabled)
139 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
140
141 anchors {
142 fill: parent
143 topMargin: panel.panelHeight
144 }
145
146 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
147 opacity: 1.0 - disappearingAnimationProgress
148 property real disappearingAnimationProgress: stages.showProgress
149
150 // FIXME: only necessary because stages.showProgress is not animated
151 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
152 }
153 }
154
155 EdgeDragArea {
156 id: stagesDragArea
157 direction: Direction.Leftwards
158
159 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
160 width: shell.edgeSize
161
162 property real progress: stages.width
163
164 onTouchXChanged: {
165 if (status == DirectionalDragArea.Recognized) {
166 if (ApplicationManager.empty) {
167 progress = Math.max(stages.width - stagesDragArea.width + touchX, stages.width * .3);
168 } else {
169 progress = stages.width - stagesDragArea.width + touchX;
170 }
171 }
172 }
173
174 onDraggingChanged: {
175 if (!dragging) {
176 if (!ApplicationManager.empty && progress < stages.width - units.gu(10)) {
177 stages.show();
178 }
179 stagesDragArea.progress = Qt.binding(function () { return stages.width; });
180 }
181 }
182 }132 }
183133
184 Item {134 Item {
@@ -186,55 +136,7 @@
186 objectName: "stages"136 objectName: "stages"
187 width: parent.width137 width: parent.width
188 height: parent.height138 height: parent.height
189139 visible: !ApplicationManager.empty
190 visible: !fullyHidden && !ApplicationManager.empty
191
192 x: {
193 if (shown) {
194 if (locked || greeter.fakeActiveForApp !== "") {
195 return 0;
196 }
197 return launcher.progress;
198 } else {
199 return stagesDragArea.progress
200 }
201 }
202 Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
203
204 property bool shown: false
205 onShownChanged: {
206 if (shown) {
207 if (ApplicationManager.count > 0) {
208 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
209 }
210 } else {
211 if (ApplicationManager.focusedApplicationId) {
212 ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
213 ApplicationManager.unfocusCurrentApplication();
214 }
215 }
216 }
217
218 // Avoid a silent "divide by zero -> NaN" situation during init as shell.width will be
219 // zero. That breaks the property binding and the function won't be reevaluated once
220 // shell.width is set, with the NaN result staying there for good.
221 property real showProgress: shell.width ? MathUtils.clamp(1 - x / shell.width, 0, 1) : 0
222
223 property bool fullyShown: x == 0
224 property bool fullyHidden: x == width
225
226 property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
227
228 // It might technically not be fullyShown but visually it just looks so.
229 property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
230
231 function show() {
232 shown = true;
233 }
234
235 function hide() {
236 shown = false;
237 }
238140
239 Connections {141 Connections {
240 target: ApplicationManager142 target: ApplicationManager
@@ -243,7 +145,6 @@
243 lockscreen.show();145 lockscreen.show();
244 }146 }
245 greeter.hide();147 greeter.hide();
246 stages.show();
247 }148 }
248149
249 onFocusedApplicationIdChanged: {150 onFocusedApplicationIdChanged: {
@@ -254,18 +155,9 @@
254 }155 }
255156
256 onApplicationAdded: {157 onApplicationAdded: {
257 if (greeter.shown) {158 if (greeter.shown && appId != "unity8-dash") {
258 greeter.hide();159 greeter.hide();
259 }160 }
260 if (!stages.shown) {
261 stages.show();
262 }
263 }
264
265 onEmptyChanged: {
266 if (ApplicationManager.empty) {
267 stages.hide();
268 }
269 }161 }
270 }162 }
271163
@@ -294,14 +186,18 @@
294 Binding {186 Binding {
295 target: applicationsDisplayLoader.item187 target: applicationsDisplayLoader.item
296 property: "interactive"188 property: "interactive"
297 value: stages.roughlyFullyShown && !greeter.shown && !lockscreen.shown189 value: !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
298 && panel.indicators.fullyClosed
299 }190 }
300 Binding {191 Binding {
301 target: applicationsDisplayLoader.item192 target: applicationsDisplayLoader.item
302 property: "spreadEnabled"193 property: "spreadEnabled"
303 value: greeter.fakeActiveForApp === "" // to support emergency dialer hack194 value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
304 }195 }
196 Binding {
197 target: applicationsDisplayLoader.item
198 property: "inverseProgress"
199 value: launcher.progress
200 }
305 }201 }
306 }202 }
307203
@@ -525,7 +421,7 @@
525421
526 var animate = !LightDM.Greeter.active && !stages.shown422 var animate = !LightDM.Greeter.active && !stages.shown
527 dash.setCurrentScope("clickscope", animate, false)423 dash.setCurrentScope("clickscope", animate, false)
528 stages.hide()424 ApplicationManager.requestFocusApplication("unity8-dash")
529 }425 }
530426
531 function showDash() {427 function showDash() {
@@ -534,7 +430,7 @@
534 }430 }
535431
536 if (!stages.locked) {432 if (!stages.locked) {
537 stages.hide();433 ApplicationManager.requestFocusApplication("unity8-dash")
538 launcher.fadeOut();434 launcher.fadeOut();
539 } else {435 } else {
540 launcher.switchToNextState("visible");436 launcher.switchToNextState("visible");
@@ -568,7 +464,7 @@
568 ApplicationManager.focusedApplicationId &&464 ApplicationManager.focusedApplicationId &&
569 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen465 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
570466
571 fullscreenMode: (stages.roughlyFullyShown && topmostApplicationIsFullscreen467 fullscreenMode: (topmostApplicationIsFullscreen
572 && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""468 && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""
573 }469 }
574470
@@ -586,7 +482,11 @@
586482
587 onShowDashHome: showHome()483 onShowDashHome: showHome()
588 onDash: showDash()484 onDash: showDash()
589 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)485 onDashSwipeChanged: {
486 if (dashSwipe && ApplicationManager.focusedApplicationId !== "unity8-dash") {
487 dash.setCurrentScope("clickscope", false, true)
488 }
489 }
590 onLauncherApplicationSelected: {490 onLauncherApplicationSelected: {
591 if (greeter.fakeActiveForApp !== "") {491 if (greeter.fakeActiveForApp !== "") {
592 lockscreen.show()492 lockscreen.show()
@@ -678,7 +578,6 @@
678 z: alphaDisclaimerLabel.z + 10578 z: alphaDisclaimerLabel.z + 10
679 greeter: greeter579 greeter: greeter
680 launcher: launcher580 launcher: launcher
681 dash: dash
682 indicators: panel.indicators581 indicators: panel.indicators
683 underlay: underlay582 underlay: underlay
684 }583 }
685584
=== modified file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 2014-07-28 15:50:19 +0000
+++ qml/Stages/PhoneStage.qml 2014-07-28 15:50:22 +0000
@@ -21,7 +21,7 @@
21import Utils 0.121import Utils 0.1
22import "../Components"22import "../Components"
2323
24Item {24Rectangle {
25 id: root25 id: root
2626
27 // Controls to be set from outside27 // Controls to be set from outside
@@ -29,10 +29,13 @@
29 property real maximizedAppTopMargin29 property real maximizedAppTopMargin
30 property bool interactive30 property bool interactive
31 property bool spreadEnabled: true // If false, animations and right edge will be disabled31 property bool spreadEnabled: true // If false, animations and right edge will be disabled
32 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
3233
33 // State information propagated to the outside34 // State information propagated to the outside
34 readonly property bool locked: spreadView.phase == 235 readonly property bool locked: spreadView.phase == 2
3536
37 color: "black"
38
36 function select(appId) {39 function select(appId) {
37 spreadView.snapTo(priv.indexOf(appId));40 spreadView.snapTo(priv.indexOf(appId));
38 }41 }
@@ -43,6 +46,16 @@
43 spreadView.contentX = -spreadView.shift;46 spreadView.contentX = -spreadView.shift;
44 }47 }
4548
49 onInverseProgressChanged: {
50 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
51 // left edge drag released. Minimum distance is given by design.
52 if (priv.oldInverseProgress > units.gu(22)) {
53 ApplicationManager.focusApplication("unity8-dash");
54 }
55 }
56 priv.oldInverseProgress = inverseProgress;
57 }
58
46 Connections {59 Connections {
47 target: ApplicationManager60 target: ApplicationManager
4861
@@ -50,7 +63,7 @@
50 if (spreadView.phase > 0) {63 if (spreadView.phase > 0) {
51 spreadView.snapTo(priv.indexOf(appId));64 spreadView.snapTo(priv.indexOf(appId));
52 } else {65 } else {
53 priv.switchToApp(appId);66 ApplicationManager.focusApplication(appId);
54 }67 }
55 }68 }
5669
@@ -60,7 +73,7 @@
60 } else {73 } else {
61 spreadView.phase = 0;74 spreadView.phase = 0;
62 spreadView.contentX = -spreadView.shift;75 spreadView.contentX = -spreadView.shift;
63 priv.switchToApp(appId);76 ApplicationManager.focusApplication(appId);
64 }77 }
65 }78 }
6679
@@ -79,15 +92,11 @@
7992
80 property string focusedAppId: ApplicationManager.focusedApplicationId93 property string focusedAppId: ApplicationManager.focusedApplicationId
81 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)94 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
8295 property var focusedAppDelegate: null
83 function switchToApp(appId) {96
84 if (priv.focusedAppId) {97 property real oldInverseProgress: 0
85 spreadView.focusChanging = true;98
86 ApplicationManager.focusApplication(appId);99 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
87 } else {
88 ApplicationManager.focusApplication(appId);
89 }
90 }
91100
92 function indexOf(appId) {101 function indexOf(appId) {
93 for (var i = 0; i < ApplicationManager.count; i++) {102 for (var i = 0; i < ApplicationManager.count; i++) {
@@ -100,14 +109,6 @@
100109
101 }110 }
102111
103 Rectangle {
104 id: coverFlipBackground
105 anchors.fill: parent
106 color: "black"
107 visible: spreadView.visible
108 }
109
110
111 Flickable {112 Flickable {
112 id: spreadView113 id: spreadView
113 objectName: "spreadView"114 objectName: "spreadView"
@@ -116,6 +117,8 @@
116 contentWidth: spreadRow.width - shift117 contentWidth: spreadRow.width - shift
117 contentX: -shift118 contentX: -shift
118119
120 readonly property bool isActive: shiftedContentX > 0 || spreadDragArea.dragging
121
119 // The flickable needs to fill the screen in order to get touch events all over.122 // The flickable needs to fill the screen in order to get touch events all over.
120 // However, we don't want to the user to be able to scroll back all the way. For123 // However, we don't want to the user to be able to scroll back all the way. For
121 // that, the beginning of the gesture starts with a negative value for contentX124 // that, the beginning of the gesture starts with a negative value for contentX
@@ -198,6 +201,12 @@
198 snapAnimation.start();201 snapAnimation.start();
199 }202 }
200203
204 // In case the ApplicationManager already holds an app when starting up we're missing animations
205 // Make sure we end up in the same state
206 Component.onCompleted: {
207 spreadView.contentX = -spreadView.shift
208 }
209
201 SequentialAnimation {210 SequentialAnimation {
202 id: snapAnimation211 id: snapAnimation
203 property int targetContentX: -spreadView.shift212 property int targetContentX: -spreadView.shift
@@ -260,14 +269,33 @@
260 && spreadView.shiftedContentX === 0 && root.interactive && index === 0269 && spreadView.shiftedContentX === 0 && root.interactive && index === 0
261 swipeToCloseEnabled: spreadView.interactive270 swipeToCloseEnabled: spreadView.interactive
262 maximizedAppTopMargin: root.maximizedAppTopMargin271 maximizedAppTopMargin: root.maximizedAppTopMargin
263 dropShadow: spreadView.shiftedContentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided272 dropShadow: spreadView.isActive ||
264273 priv.focusedAppDelegate.x !== 0
265 z: behavioredIndex274
266 x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance275 readonly property bool isDash: model.appId == "unity8-dash"
276
277 z: isDash && !spreadView.isActive ? -1 : behavioredIndex
278
279 x: {
280 // focused app is always positioned at 0 except when following left edge drag
281 if (index == 0) {
282 if (!isDash && root.inverseProgress > 0) {
283 return root.inverseProgress;
284 }
285 return 0;
286 }
287 if (isDash && !spreadView.isActive && !spreadDragArea.dragging) {
288 return 0;
289 }
290
291 // Otherwise line up for the spread
292 return spreadView.width + (index - 1) * spreadView.tileDistance;
293 }
267 property real behavioredIndex: index294 property real behavioredIndex: index
268 Behavior on behavioredIndex {295 Behavior on behavioredIndex {
269 enabled: spreadView.closingIndex >= 0296 enabled: spreadView.closingIndex >= 0
270 UbuntuNumberAnimation {297 UbuntuNumberAnimation {
298 id: appXAnimation
271 onRunningChanged: {299 onRunningChanged: {
272 if (!running) {300 if (!running) {
273 spreadView.closingIndex = -1;301 spreadView.closingIndex = -1;
@@ -277,11 +305,13 @@
277 }305 }
278306
279 Behavior on x {307 Behavior on x {
280 enabled: spreadView.focusChanging && index == 0 && root.spreadEnabled308 enabled: root.spreadEnabled &&
309 !spreadView.isActive &&
310 !snapAnimation.running
281 UbuntuNumberAnimation {311 UbuntuNumberAnimation {
282 duration: UbuntuAnimation.FastDuration312 duration: UbuntuAnimation.FastDuration
283 onRunningChanged: {313 onRunningChanged: {
284 if (!running) {314 if (!running && root.inverseProgress == 0) {
285 spreadView.focusChanging = false;315 spreadView.focusChanging = false;
286 }316 }
287 }317 }
@@ -322,6 +352,10 @@
322 return progress;352 return progress;
323 }353 }
324354
355 // Hiding tiles when their progress is negative or reached the maximum
356 visible: (progress >= 0 && progress < 1.7) ||
357 (model.appId == "unity8-dash" && priv.focusedAppDelegate.x !== 0)
358
325 EasingCurve {359 EasingCurve {
326 id: snappingCurve360 id: snappingCurve
327 type: EasingCurve.Linear361 type: EasingCurve.Linear
328362
=== modified file 'qml/Stages/SpreadDelegate.qml'
--- qml/Stages/SpreadDelegate.qml 2014-07-28 15:50:19 +0000
+++ qml/Stages/SpreadDelegate.qml 2014-07-28 15:50:22 +0000
@@ -37,6 +37,7 @@
3737
38 SurfaceContainer {38 SurfaceContainer {
39 id: surfaceContainer39 id: surfaceContainer
40 objectName: "surfaceContainer"
40 anchors.fill: parent41 anchors.fill: parent
41 surface: model.surface42 surface: model.surface
42 property bool appHasCreatedASurface: false43 property bool appHasCreatedASurface: false
@@ -172,6 +173,11 @@
172 }173 }
173174
174 onDragEnd: {175 onDragEnd: {
176 if (model.appId == "unity8-dash") {
177 animation.animate("center")
178 return;
179 }
180
175 // velocity and distance values specified by design prototype181 // velocity and distance values specified by design prototype
176 if ((dragVelocity < -units.gu(40) && distance < -units.gu(8)) || distance < -root.height / 2) {182 if ((dragVelocity < -units.gu(40) && distance < -units.gu(8)) || distance < -root.height / 2) {
177 animation.animate("up")183 animation.animate("up")
178184
=== modified file 'qml/Stages/TabletStage.qml'
--- qml/Stages/TabletStage.qml 2014-07-28 15:50:19 +0000
+++ qml/Stages/TabletStage.qml 2014-07-28 15:50:22 +0000
@@ -21,10 +21,11 @@
21import Utils 0.121import Utils 0.1
22import "../Components"22import "../Components"
2323
24Item {24Rectangle {
25 id: root25 id: root
26 objectName: "stages"26 objectName: "stages"
27 anchors.fill: parent27 anchors.fill: parent
28 color: "black"
2829
29 // Controls to be set from outside30 // Controls to be set from outside
30 property bool shown: false31 property bool shown: false
@@ -32,10 +33,21 @@
32 property int dragAreaWidth33 property int dragAreaWidth
33 property real maximizedAppTopMargin34 property real maximizedAppTopMargin
34 property bool interactive35 property bool interactive
36 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
3537
36 // State information propagated to the outside38 // State information propagated to the outside
37 readonly property bool locked: spreadView.phase == 239 readonly property bool locked: spreadView.phase == 2
3840
41 onInverseProgressChanged: {
42 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
43 // left edge drag released. Minimum distance is given by design.
44 if (priv.oldInverseProgress > units.gu(22)) {
45 ApplicationManager.focusApplication("unity8-dash");
46 }
47 }
48 priv.oldInverseProgress = inverseProgress;
49 }
50
39 QtObject {51 QtObject {
40 id: priv52 id: priv
4153
@@ -49,6 +61,8 @@
49 property string appId061 property string appId0
50 property string appId162 property string appId1
5163
64 property int oldInverseProgress: 0
65
52 onFocusedAppIdChanged: {66 onFocusedAppIdChanged: {
53 if (priv.focusedAppId.length > 0) {67 if (priv.focusedAppId.length > 0) {
54 var focusedApp = ApplicationManager.findApplication(focusedAppId);68 var focusedApp = ApplicationManager.findApplication(focusedAppId);
@@ -107,16 +121,26 @@
107 }121 }
108 }122 }
109123
124 onApplicationAdded: {
125 if (spreadView.phase == 2) {
126 spreadView.snapTo(ApplicationManager.count - 1);
127 } else {
128 spreadView.phase = 0;
129 spreadView.contentX = -spreadView.shift;
130 ApplicationManager.focusApplication(appId);
131 }
132 }
133
110 onApplicationRemoved: {134 onApplicationRemoved: {
111 if (priv.mainStageAppId == appId) {135 if (priv.mainStageAppId == appId) {
112 priv.mainStageAppId = "";136 ApplicationManager.focusApplication("unity8-dash")
113 }137 }
114 if (priv.sideStageAppId == appId) {138 if (priv.sideStageAppId == appId) {
115 priv.sideStageAppId = "";139 priv.sideStageAppId = "";
116 }140 }
117 if (ApplicationManager.count == 0) {141 if (ApplicationManager.count == 0) {
118 spreadView.phase = 0;142 spreadView.phase = 0;
119 spreadView.contentX = 0;143 spreadView.contentX = -spreadView.shift;
120 }144 }
121 }145 }
122 }146 }
@@ -124,13 +148,25 @@
124 Flickable {148 Flickable {
125 id: spreadView149 id: spreadView
126 anchors.fill: parent150 anchors.fill: parent
127 contentWidth: spreadRow.width
128 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1151 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
152 contentWidth: spreadRow.width - shift
153 contentX: -shift
129154
130 property int tileDistance: units.gu(20)155 property int tileDistance: units.gu(20)
131 property int sideStageWidth: units.gu(40)156 property int sideStageWidth: units.gu(40)
132 property bool sideStageVisible: priv.sideStageAppId157 property bool sideStageVisible: priv.sideStageAppId
133158
159 readonly property bool isActive: shiftedContentX > 0 || spreadDragArea.dragging
160 onIsActiveChanged: print("spreadView isActive changed", isActive)
161
162 // The flickable needs to fill the screen in order to get touch events all over.
163 // However, we don't want to the user to be able to scroll back all the way. For
164 // that, the beginning of the gesture starts with a negative value for contentX
165 // so the flickable wants to pull it into the view already. "shift" tunes the
166 // distance where to "lock" the content.
167 readonly property real shift: width / 2
168 readonly property real shiftedContentX: contentX + shift
169
134 // Phase of the animation:170 // Phase of the animation:
135 // 0: Starting from right edge, a new app (index 1) comes in from the right171 // 0: Starting from right edge, a new app (index 1) comes in from the right
136 // 1: The app has reached the first snap position.172 // 1: The app has reached the first snap position.
@@ -168,6 +204,12 @@
168 }204 }
169 }205 }
170206
207 // In case the ApplicationManager already holds an app when starting up we're missing animations
208 // Make sure we end up in the same state
209 Component.onCompleted: {
210 spreadView.contentX = -spreadView.shift
211 }
212
171 property int nextInStack: {213 property int nextInStack: {
172 switch (state) {214 switch (state) {
173 case "main":215 case "main":
@@ -224,31 +266,31 @@
224 return "empty";266 return "empty";
225 }267 }
226268
227 onContentXChanged: {269 onShiftedContentXChanged: {
228 if (spreadView.phase == 0 && spreadView.contentX > spreadView.width * spreadView.positionMarker2) {270 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
229 spreadView.phase = 1;271 spreadView.phase = 1;
230 } else if (spreadView.phase == 1 && spreadView.contentX > spreadView.width * spreadView.positionMarker4) {272 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
231 spreadView.phase = 2;273 spreadView.phase = 2;
232 } else if (spreadView.phase == 1 && spreadView.contentX < spreadView.width * spreadView.positionMarker2) {274 } else if (spreadView.phase == 1 && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
233 spreadView.phase = 0;275 spreadView.phase = 0;
234 }276 }
235 }277 }
236278
237 function snap() {279 function snap() {
238 if (contentX < phase0Width) {280 if (shiftedContentX < phase0Width) {
239 snapAnimation.targetContentX = 0;281 snapAnimation.targetContentX = -shift;
240 snapAnimation.start();282 snapAnimation.start();
241 } else if (contentX < phase1Width) {283 } else if (shiftedContentX < phase1Width) {
242 snapTo(1);284 snapTo(1);
243 } else {285 } else {
244 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.286 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
245 snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1;287 snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1 - shift;
246 snapAnimation.start();288 snapAnimation.start();
247 }289 }
248 }290 }
249 function snapTo(index) {291 function snapTo(index) {
250 spreadView.selectedIndex = index;292 spreadView.selectedIndex = index;
251 snapAnimation.targetContentX = 0;293 snapAnimation.targetContentX = -shift;
252 snapAnimation.start();294 snapAnimation.start();
253 }295 }
254296
@@ -309,7 +351,7 @@
309351
310 SequentialAnimation {352 SequentialAnimation {
311 id: snapAnimation353 id: snapAnimation
312 property int targetContentX: 0354 property int targetContentX: -spreadView.shift
313355
314 UbuntuNumberAnimation {356 UbuntuNumberAnimation {
315 target: spreadView357 target: spreadView
@@ -325,15 +367,14 @@
325 spreadView.selectedIndex = -1;367 spreadView.selectedIndex = -1;
326 ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId);368 ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId);
327 spreadView.phase = 0;369 spreadView.phase = 0;
328 spreadView.contentX = 0;370 spreadView.contentX = -spreadView.shift;
329 }371 }
330 }372 }
331 }373 }
332 }374 }
333375
334 Rectangle {376 Item {
335 id: spreadRow377 id: spreadRow
336 color: "black"
337 x: spreadView.contentX378 x: spreadView.contentX
338 height: root.height379 height: root.height
339 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)380 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
@@ -380,7 +421,7 @@
380 MouseArea {421 MouseArea {
381 id: sideStageDragHandleMouseArea422 id: sideStageDragHandleMouseArea
382 anchors.fill: parent423 anchors.fill: parent
383 enabled: spreadView.contentX == 0424 enabled: spreadView.shiftedContentX == 0
384 property int startX425 property int startX
385 property var gesturePoints: new Array()426 property var gesturePoints: new Array()
386427
@@ -413,7 +454,7 @@
413454
414 onRunningChanged: {455 onRunningChanged: {
415 if (!running) {456 if (!running) {
416 sideStageDragHandle.dragging = false;;457 sideStageDragHandle.dragging = false;
417 }458 }
418 }459 }
419 }460 }
@@ -427,17 +468,26 @@
427 id: spreadTile468 id: spreadTile
428 height: spreadView.height469 height: spreadView.height
429 width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth470 width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
430 x: spreadView.width
431 z: spreadView.indexToZIndex(index)
432 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId471 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
433 zIndex: z472 zIndex: spreadView.indexToZIndex(index)
434 selected: spreadView.selectedIndex == index473 selected: spreadView.selectedIndex == index
435 otherSelected: spreadView.selectedIndex >= 0 && !selected474 otherSelected: spreadView.selectedIndex >= 0 && !selected
436 isInSideStage: priv.sideStageAppId == model.appId475 isInSideStage: priv.sideStageAppId == model.appId
437 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive476 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
438 swipeToCloseEnabled: spreadView.interactive477 swipeToCloseEnabled: spreadView.interactive
439 maximizedAppTopMargin: root.maximizedAppTopMargin478 maximizedAppTopMargin: root.maximizedAppTopMargin
440 dropShadow: spreadView.contentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided479 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 ? root.inverseProgress : 0
480
481 readonly property bool isDash: model.appId == "unity8-dash"
482
483 // FIXME: A regular binding doesn't update any more after closing an app.
484 // Using a Binding for now.
485 Binding {
486 target: spreadTile
487 property: "z"
488 value: (!spreadView.isActive && isDash && !active) ? -1 : spreadTile.zIndex
489 }
490 x: spreadView.width
441491
442 property real behavioredZIndex: zIndex492 property real behavioredZIndex: zIndex
443 Behavior on behavioredZIndex {493 Behavior on behavioredZIndex {
@@ -458,7 +508,7 @@
458 }508 }
459509
460 progress: {510 progress: {
461 var tileProgress = (spreadView.contentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;511 var tileProgress = (spreadView.shiftedContentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
462 // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0512 // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
463 if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {513 if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
464 tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;514 tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
@@ -481,11 +531,7 @@
481531
482 onClicked: {532 onClicked: {
483 if (spreadView.phase == 2) {533 if (spreadView.phase == 2) {
484 if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {534 spreadView.snapTo(index);
485 spreadView.snapTo(index);
486 } else {
487 ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
488 }
489 }535 }
490 }536 }
491537
@@ -518,12 +564,12 @@
518 onTouchXChanged: {564 onTouchXChanged: {
519 if (!dragging) {565 if (!dragging) {
520 spreadView.phase = 0;566 spreadView.phase = 0;
521 spreadView.contentX = 0;567 spreadView.contentX = -spreadView.shift;
522 }568 }
523569
524 if (attachedToView) {570 if (dragging && attachedToView) {
525 spreadView.contentX = -touchX + spreadDragArea.width;571 spreadView.contentX = -touchX + spreadDragArea.width - spreadView.shift;
526 if (spreadView.contentX > spreadView.phase0Width + spreadView.phase1Width / 2) {572 if (spreadView.shiftedContentX > spreadView.phase0Width + spreadView.phase1Width / 2) {
527 attachedToView = false;573 attachedToView = false;
528 spreadView.snap();574 spreadView.snap();
529 }575 }
@@ -548,14 +594,14 @@
548 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);594 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
549 gesturePoints = [];595 gesturePoints = [];
550596
551 if (oneWayFlick && spreadView.contentX < spreadView.positionMarker1 * spreadView.width) {597 if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
552 // If it was a short one-way movement, do the Alt+Tab switch598 // If it was a short one-way movement, do the Alt+Tab switch
553 // no matter if we didn't cross positionMarker1 yet.599 // no matter if we didn't cross positionMarker1 yet.
554 spreadView.snapTo(spreadView.nextInStack);600 spreadView.snapTo(spreadView.nextInStack);
555 } else if (!dragging && attachedToView) {601 } else if (!dragging && attachedToView) {
556 if (spreadView.contentX < spreadView.width * spreadView.positionMarker1) {602 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
557 spreadView.snap();603 spreadView.snap();
558 } else if (spreadView.contentX < spreadView.width * spreadView.positionMarker2) {604 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
559 spreadView.snapTo(spreadView.nextInStack);605 spreadView.snapTo(spreadView.nextInStack);
560 } else {606 } else {
561 // otherwise snap to the closest snap position we can find607 // otherwise snap to the closest snap position we can find
562608
=== modified file 'qml/Stages/TransformedSpreadDelegate.qml'
--- qml/Stages/TransformedSpreadDelegate.qml 2014-07-28 15:50:19 +0000
+++ qml/Stages/TransformedSpreadDelegate.qml 2014-07-28 15:50:22 +0000
@@ -46,9 +46,6 @@
46 property real startDistance: units.gu(5)46 property real startDistance: units.gu(5)
47 property real endDistance: units.gu(.5)47 property real endDistance: units.gu(.5)
4848
49 // Hiding tiles when their progress is negative or reached the maximum
50 visible: progress >= 0 && progress < 1.7
51
52 onSelectedChanged: {49 onSelectedChanged: {
53 if (selected) {50 if (selected) {
54 priv.snapshot();51 priv.snapshot();
@@ -132,6 +129,10 @@
132 // the selected tile, which is animated from the snapshotted position to be fullscreen.129 // the selected tile, which is animated from the snapshotted position to be fullscreen.
133130
134 readonly property real xTranslate: {131 readonly property real xTranslate: {
132 if (!spreadView.isActive) {
133 return 0;
134 }
135
135 if (otherSelected) {136 if (otherSelected) {
136 if (spreadView.phase < 2 && index == 0) {137 if (spreadView.phase < 2 && index == 0) {
137 return linearAnimation(selectedProgress, 0, selectedXTranslate,138 return linearAnimation(selectedProgress, 0, selectedXTranslate,
@@ -183,7 +184,7 @@
183 }184 }
184185
185 readonly property real angle: {186 readonly property real angle: {
186 if (spreadView.focusChanging) {187 if (!spreadView.isActive) {
187 return 0;188 return 0;
188 }189 }
189190
@@ -216,7 +217,7 @@
216 }217 }
217218
218 readonly property real scale: {219 readonly property real scale: {
219 if (spreadView.focusChanging) {220 if (!spreadView.isActive) {
220 return 1;221 return 1;
221 }222 }
222 if (priv.otherSelected) {223 if (priv.otherSelected) {
223224
=== modified file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
--- qml/Stages/TransformedTabletSpreadDelegate.qml 2014-07-28 15:50:19 +0000
+++ qml/Stages/TransformedTabletSpreadDelegate.qml 2014-07-28 15:50:22 +0000
@@ -47,6 +47,11 @@
4747
48 property bool isInSideStage: false48 property bool isInSideStage: false
4949
50 property int dragOffset: 0
51
52 dropShadow: spreadView.isActive ||
53 (active && model.stage == ApplicationInfoInterface.MainStage && priv.xTranslate != 0)
54
50 onSelectedChanged: {55 onSelectedChanged: {
51 if (selected) {56 if (selected) {
52 priv.snapshot();57 priv.snapshot();
@@ -129,6 +134,16 @@
129 return helperEasingCurve.value * (endValue - startValue) + startValue;134 return helperEasingCurve.value * (endValue - startValue) + startValue;
130 }135 }
131136
137 Behavior on xTranslate {
138 enabled: !spreadView.isActive &&
139 !snapAnimation.running &&
140 model.appId !== "unity8-dash" &&
141 !spreadView.sideStageDragging
142 UbuntuNumberAnimation {
143 duration: UbuntuAnimation.FastDuration
144 }
145 }
146
132 property real xTranslate: {147 property real xTranslate: {
133 var newTranslate = 0;148 var newTranslate = 0;
134149
@@ -155,7 +170,12 @@
155 if (spreadView.phase == 0 && shouldMoveAway) {170 if (spreadView.phase == 0 && shouldMoveAway) {
156 newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress);171 newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress);
157 }172 }
158 }173 newTranslate += root.dragOffset;
174 }
175 if (!spreadView.isActive && model.appId == "unity8-dash" && !root.active) {
176 newTranslate -= root.width;
177 }
178
159 if (nextInStack && spreadView.phase == 0) {179 if (nextInStack && spreadView.phase == 0) {
160 if (model.stage == ApplicationInfoInterface.MainStage) {180 if (model.stage == ApplicationInfoInterface.MainStage) {
161 if (spreadView.sideStageVisible && root.progress > 0) {181 if (spreadView.sideStageVisible && root.progress > 0) {
@@ -205,6 +225,10 @@
205 }225 }
206226
207 property real scale: {227 property real scale: {
228 if (!spreadView.isActive) {
229 return 1;
230 }
231
208 if (otherSelected) {232 if (otherSelected) {
209 return selectedScale;233 return selectedScale;
210 }234 }
@@ -249,6 +273,10 @@
249 }273 }
250274
251 property real angle: {275 property real angle: {
276 if (!spreadView.isActive) {
277 return 0;
278 }
279
252 if (otherSelected) {280 if (otherSelected) {
253 return selectedAngle;281 return selectedAngle;
254 }282 }
255283
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2014-07-28 15:50:19 +0000
+++ src/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -35,3 +35,4 @@
35 )35 )
3636
37add_subdirectory(Panel)37add_subdirectory(Panel)
38add_subdirectory(Dash)
3839
=== added directory 'src/Dash'
=== added file 'src/Dash/CMakeLists.txt'
--- src/Dash/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/Dash/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -0,0 +1,12 @@
1set(DASH_SRCS
2 main.cpp
3)
4
5add_executable(unity8-dash ${DASH_SRCS})
6
7qt5_use_modules(unity8-dash Gui Qml Quick Test)
8
9# install binaries
10install(TARGETS ${DASH_APP}
11 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
12)
013
=== added file 'src/Dash/main.cpp'
--- src/Dash/main.cpp 1970-01-01 00:00:00 +0000
+++ src/Dash/main.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,49 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * Authors:
5 * Michael Zanetti <michael.zanetti@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <QtQuick/QQuickView>
21#include <QtGui/QGuiApplication>
22#include <QtQml/QQmlEngine>
23#include <QtQml/QQmlContext>
24#include <QDebug>
25
26#include <paths.h>
27
28int main(int argc, const char *argv[])
29{
30 QGuiApplication *application = new QGuiApplication(argc, (char**)argv);
31
32 QQuickView* view = new QQuickView();
33 view->setResizeMode(QQuickView::SizeRootObjectToView);
34 view->setTitle("Unity Dash");
35
36 QUrl source(::qmlDirectory()+"Dash/DashApplication.qml");
37 prependImportPaths(view->engine(), ::overrideImportPaths());
38 appendImportPaths(view->engine(), ::fallbackImportPaths());
39
40 view->setSource(source);
41 view->show();
42
43 int result = application->exec();
44
45 delete view;
46 delete application;
47
48 return result;
49}
050
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-07-28 15:50:19 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-07-28 15:50:22 +0000
@@ -31,8 +31,6 @@
31 , m_state(Starting)31 , m_state(Starting)
32 , m_focused(false)32 , m_focused(false)
33 , m_fullscreen(false)33 , m_fullscreen(false)
34 , m_windowItem(0)
35 , m_windowComponent(0)
36 , m_parentItem(0)34 , m_parentItem(0)
37 , m_surface(0)35 , m_surface(0)
38{36{
@@ -45,8 +43,6 @@
45 , m_state(Starting)43 , m_state(Starting)
46 , m_focused(false)44 , m_focused(false)
47 , m_fullscreen(false)45 , m_fullscreen(false)
48 , m_windowItem(0)
49 , m_windowComponent(0)
50 , m_parentItem(0)46 , m_parentItem(0)
51 , m_surface(0)47 , m_surface(0)
52{48{
@@ -61,12 +57,6 @@
61 }57 }
62}58}
6359
64void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)
65{
66 if (status == QQmlComponent::Ready && !m_windowItem)
67 doCreateWindowItem();
68}
69
70void ApplicationInfo::onStateChanged(State state)60void ApplicationInfo::onStateChanged(State state)
71{61{
72 if (state == ApplicationInfo::Running) {62 if (state == ApplicationInfo::Running) {
@@ -108,55 +98,3 @@
108 Q_EMIT surfaceChanged(m_surface);98 Q_EMIT surfaceChanged(m_surface);
109 SurfaceManager::singleton()->registerSurface(m_surface);99 SurfaceManager::singleton()->registerSurface(m_surface);
110}100}
111
112void ApplicationInfo::createWindowComponent()
113{
114 // The assumptions I make here really should hold.
115 QQuickView *quickView =
116 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
117
118 QQmlEngine *engine = quickView->engine();
119
120 m_windowComponent = new QQmlComponent(engine, this);
121 m_windowComponent->setData(m_windowQml.toLatin1(), QUrl());
122}
123
124void ApplicationInfo::doCreateWindowItem()
125{
126 m_windowItem = qobject_cast<QQuickItem *>(m_windowComponent->create());
127 m_windowItem->setParentItem(m_parentItem);
128}
129
130void ApplicationInfo::createWindowItem()
131{
132 if (!m_windowComponent)
133 createWindowComponent();
134
135 // only create the windowItem once the component is ready
136 if (!m_windowComponent->isReady()) {
137 connect(m_windowComponent, &QQmlComponent::statusChanged,
138 this, &ApplicationInfo::onWindowComponentStatusChanged);
139 } else {
140 doCreateWindowItem();
141 }
142}
143
144void ApplicationInfo::showWindow(QQuickItem *parent)
145{
146 m_parentItem = parent;
147
148 if (!m_windowItem)
149 createWindowItem();
150
151 if (m_windowItem) {
152 m_windowItem->setVisible(true);
153 }
154}
155
156void ApplicationInfo::hideWindow()
157{
158 if (!m_windowItem)
159 return;
160
161 m_windowItem->setVisible(false);
162}
163101
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
--- tests/mocks/Unity/Application/ApplicationInfo.h 2014-07-28 15:50:19 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-07-28 15:50:22 +0000
@@ -88,22 +88,12 @@
88Q_SIGNALS:88Q_SIGNALS:
89 void surfaceChanged(MirSurfaceItem*);89 void surfaceChanged(MirSurfaceItem*);
9090
91public:
92 void showWindow(QQuickItem *parent);
93 void hideWindow();
94
95private Q_SLOTS:91private Q_SLOTS:
96 void onWindowComponentStatusChanged(QQmlComponent::Status status);
97 void onStateChanged(State state);92 void onStateChanged(State state);
9893
99 void createSurface();94 void createSurface();
10095
101private:96private:
102 void createWindowItem();
103 void doCreateWindowItem();
104 void createWindowComponent();
105 QQuickItem *m_windowItem;
106 QQmlComponent *m_windowComponent;
107 QQuickItem *m_parentItem;97 QQuickItem *m_parentItem;
108 MirSurfaceItem* m_surface;98 MirSurfaceItem* m_surface;
109};99};
110100
=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
--- tests/mocks/Unity/Application/ApplicationManager.cpp 2014-07-28 15:50:19 +0000
+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2014-07-28 15:50:22 +0000
@@ -48,16 +48,14 @@
48ApplicationManager::ApplicationManager(QObject *parent)48ApplicationManager::ApplicationManager(QObject *parent)
49 : ApplicationManagerInterface(parent)49 : ApplicationManagerInterface(parent)
50 , m_suspended(false)50 , m_suspended(false)
51 , m_mainStageComponent(0)
52 , m_mainStage(0)
53 , m_sideStageComponent(0)
54 , m_sideStage(0)
55 , m_rightMargin(0)
56{51{
57 m_roleNames.insert(RoleSurface, "surface");52 m_roleNames.insert(RoleSurface, "surface");
58 m_roleNames.insert(RoleFullscreen, "fullscreen");53 m_roleNames.insert(RoleFullscreen, "fullscreen");
5954
60 buildListOfAvailableApplications();55 buildListOfAvailableApplications();
56
57 startApplication("unity8-dash");
58 focusApplication("unity8-dash");
61}59}
6260
63ApplicationManager::~ApplicationManager()61ApplicationManager::~ApplicationManager()
@@ -136,7 +134,6 @@
136 Q_EMIT applicationAdded(application->appId());134 Q_EMIT applicationAdded(application->appId());
137 Q_EMIT countChanged();135 Q_EMIT countChanged();
138 if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore136 if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore
139 Q_EMIT focusRequested(application->appId());
140137
141 connect(application, &ApplicationInfo::surfaceChanged, this, [application, this]() {138 connect(application, &ApplicationInfo::surfaceChanged, this, [application, this]() {
142 QModelIndex appIndex = findIndex(application);139 QModelIndex appIndex = findIndex(application);
@@ -222,6 +219,10 @@
222219
223bool ApplicationManager::stopApplication(const QString &appId)220bool ApplicationManager::stopApplication(const QString &appId)
224{221{
222 if (appId == "unity8-dash") {
223 return false;
224 }
225
225 ApplicationInfo *application = findApplication(appId);226 ApplicationInfo *application = findApplication(appId);
226 if (application == nullptr)227 if (application == nullptr)
227 return false;228 return false;
@@ -294,39 +295,25 @@
294 for (ApplicationInfo *app : m_runningApplications) {295 for (ApplicationInfo *app : m_runningApplications) {
295 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {296 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
296 app->setFocused(false);297 app->setFocused(false);
297 app->hideWindow();
298 app->setState(ApplicationInfo::Suspended);298 app->setState(ApplicationInfo::Suspended);
299 }299 }
300 }300 }
301301
302 // focus this app302 // focus this app
303 application->setFocused(true);303 application->setFocused(true);
304 if (!m_mainStage)
305 createMainStage();
306 application->setState(ApplicationInfo::Running);304 application->setState(ApplicationInfo::Running);
307 application->showWindow(m_mainStage);
308 m_mainStage->setZ(-1000);
309 if (m_sideStage)
310 m_sideStage->setZ(-2000);
311 } else if (application->stage() == ApplicationInfo::SideStage) {305 } else if (application->stage() == ApplicationInfo::SideStage) {
312 // unfocus currently focused sidestage app306 // unfocus currently focused sidestage app
313 for (ApplicationInfo *app : m_runningApplications) {307 for (ApplicationInfo *app : m_runningApplications) {
314 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {308 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
315 app->setFocused(false);309 app->setFocused(false);
316 app->hideWindow();
317 app->setState(ApplicationInfo::Suspended);310 app->setState(ApplicationInfo::Suspended);
318 }311 }
319 }312 }
320313
321 // focus this app314 // focus this app
322 application->setFocused(true);315 application->setFocused(true);
323 if (!m_sideStage)
324 createSideStage();
325 application->setState(ApplicationInfo::Running);316 application->setState(ApplicationInfo::Running);
326 application->showWindow(m_sideStage);
327 m_sideStage->setZ(-1000);
328 if (m_mainStage)
329 m_mainStage->setZ(-2000);
330 }317 }
331318
332 // move app to top of stack319 // move app to top of stack
@@ -348,7 +335,6 @@
348{335{
349 for (ApplicationInfo *app : m_runningApplications) {336 for (ApplicationInfo *app : m_runningApplications) {
350 if (app->focused()) {337 if (app->focused()) {
351 app->hideWindow();
352 app->setFocused(false);338 app->setFocused(false);
353 }339 }
354 }340 }
@@ -357,42 +343,6 @@
357343
358void ApplicationManager::generateQmlStrings(ApplicationInfo *application)344void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
359{345{
360 // TODO: Is there a better way of solving this fullscreen vs. regular
361 // application height?
362 QString topMargin;
363 if (application->fullscreen()) {
364 topMargin.append("0");
365 } else {
366 // Taken from Panel.panelHeight
367 topMargin.append("units.gu(3) + units.dp(2)");
368 }
369
370 QString windowQml = QString(
371 "import QtQuick 2.0\n"
372 "Image {\n"
373 " anchors.fill: parent\n"
374 " anchors.topMargin: %1\n"
375 " anchors.rightMargin: %2\n"
376 " source: \"file://%3/Dash/graphics/phone/screenshots/%4.png\"\n"
377 " smooth: true\n"
378 " fillMode: Image.PreserveAspectCrop\n"
379 "}").arg(topMargin)
380 .arg(m_rightMargin)
381 .arg(qmlDirectory())
382 .arg(application->icon().toString());
383 application->setWindowQml(windowQml);
384
385 QString imageQml = QString(
386 "import QtQuick 2.0\n"
387 "Image {\n"
388 " anchors.fill: parent\n"
389 " source: \"file://%1/Dash/graphics/phone/screenshots/%2.png\"\n"
390 " smooth: true\n"
391 " fillMode: Image.PreserveAspectCrop\n"
392 "}").arg(qmlDirectory())
393 .arg(application->icon().toString());
394 application->setImageQml(imageQml);
395
396 application->setScreenshot(QString("file://%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory())346 application->setScreenshot(QString("file://%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory())
397 .arg(application->icon().toString()));347 .arg(application->icon().toString()));
398}348}
@@ -402,6 +352,14 @@
402 ApplicationInfo *application;352 ApplicationInfo *application;
403353
404 application = new ApplicationInfo(this);354 application = new ApplicationInfo(this);
355 application->setAppId("unity8-dash");
356 application->setName("Unity 8 Mock Dash");
357 application->setIcon(QUrl("unity8-dash"));
358 application->setStage(ApplicationInfo::MainStage);
359 generateQmlStrings(application);
360 m_availableApplications.append(application);
361
362 application = new ApplicationInfo(this);
405 application->setAppId("dialer-app");363 application->setAppId("dialer-app");
406 application->setName("Dialer");364 application->setName("Dialer");
407 application->setIcon(QUrl("dialer"));365 application->setIcon(QUrl("dialer"));
@@ -521,78 +479,6 @@
521 m_availableApplications.append(application);479 m_availableApplications.append(application);
522}480}
523481
524void ApplicationManager::createMainStageComponent()
525{
526 // The assumptions I make here really should hold.
527 QQuickView *quickView =
528 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
529
530 QQmlEngine *engine = quickView->engine();
531
532 m_mainStageComponent = new QQmlComponent(engine, this);
533 QString mainStageQml =
534 "import QtQuick 2.0\n"
535 "Rectangle {\n"
536 " anchors.fill: parent\n"
537 " color: 'black'\n"
538 " z: -2000\n"
539 "}\n";
540 m_mainStageComponent->setData(mainStageQml.toLatin1(), QUrl());
541}
542
543void ApplicationManager::createMainStage()
544{
545 if (!m_mainStageComponent)
546 createMainStageComponent();
547
548 // The assumptions I make here really should hold.
549 QQuickView *quickView =
550 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
551
552 QQuickItem *shell = quickView->rootObject();
553
554 m_mainStage = qobject_cast<QQuickItem *>(m_mainStageComponent->create());
555 m_mainStage->setParentItem(shell);
556}
557
558void ApplicationManager::createSideStageComponent()
559{
560 // The assumptions I make here really should hold.
561 QQuickView *quickView =
562 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
563
564 QQmlEngine *engine = quickView->engine();
565
566 m_sideStageComponent = new QQmlComponent(engine, this);
567 QString sideStageQml =
568 "import QtQuick 2.0\n"
569 "import Ubuntu.Components 0.1\n"
570 "Item {\n"
571 " width: units.gu(40)\n" // from SideStage in Shell.qml
572 " anchors.top: parent.top\n"
573 " anchors.bottom: parent.bottom\n"
574 " anchors.right: parent.right\n"
575 " z: -1000\n"
576 "}\n";
577 m_sideStageComponent->setData(sideStageQml.toLatin1(), QUrl());
578}
579
580void ApplicationManager::createSideStage()
581{
582 if (!m_sideStageComponent)
583 createSideStageComponent();
584
585 // The assumptions I make here really should hold.
586 QQuickView *quickView =
587 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
588
589 QQuickItem *shell = quickView->rootObject();
590
591 m_sideStage = qobject_cast<QQuickItem *>(m_sideStageComponent->create());
592 m_sideStage->setParentItem(shell);
593 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);
594}
595
596QStringList ApplicationManager::availableApplications()482QStringList ApplicationManager::availableApplications()
597{483{
598 QStringList appIds;484 QStringList appIds;
@@ -602,19 +488,6 @@
602 return appIds;488 return appIds;
603}489}
604490
605int ApplicationManager::rightMargin() const
606{
607 return m_rightMargin;
608}
609
610void ApplicationManager::setRightMargin(int rightMargin)
611{
612 m_rightMargin = rightMargin;
613 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
614 generateQmlStrings(app);
615 }
616}
617
618bool ApplicationManager::isEmpty() const491bool ApplicationManager::isEmpty() const
619{492{
620 return m_runningApplications.isEmpty();493 return m_runningApplications.isEmpty();
621494
=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
--- tests/mocks/Unity/Application/ApplicationManager.h 2014-07-28 15:50:19 +0000
+++ tests/mocks/Unity/Application/ApplicationManager.h 2014-07-28 15:50:22 +0000
@@ -44,10 +44,6 @@
4444
45 Q_PROPERTY(bool fake READ fake CONSTANT)45 Q_PROPERTY(bool fake READ fake CONSTANT)
4646
47 // Only for testing
48 // This can be used to place some controls to right, like make tryPhoneStage for example
49 Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin)
50
51 public:47 public:
52 ApplicationManager(QObject *parent = NULL);48 ApplicationManager(QObject *parent = NULL);
53 virtual ~ApplicationManager();49 virtual ~ApplicationManager();
@@ -108,8 +104,6 @@
108104
109 // Only for testing105 // Only for testing
110 Q_INVOKABLE QStringList availableApplications();106 Q_INVOKABLE QStringList availableApplications();
111 int rightMargin() const;
112 void setRightMargin(int rightMargin);
113107
114 QModelIndex findIndex(ApplicationInfo* application);108 QModelIndex findIndex(ApplicationInfo* application);
115109
@@ -126,19 +120,9 @@
126 void showApplicationWindow(ApplicationInfo *application);120 void showApplicationWindow(ApplicationInfo *application);
127 void buildListOfAvailableApplications();121 void buildListOfAvailableApplications();
128 void generateQmlStrings(ApplicationInfo *application);122 void generateQmlStrings(ApplicationInfo *application);
129 void createMainStageComponent();
130 void createMainStage();
131 void createSideStageComponent();
132 void createSideStage();
133 bool m_suspended;123 bool m_suspended;
134 QList<ApplicationInfo*> m_runningApplications;124 QList<ApplicationInfo*> m_runningApplications;
135 QList<ApplicationInfo*> m_availableApplications;125 QList<ApplicationInfo*> m_availableApplications;
136 QQmlComponent *m_mainStageComponent;
137 QQuickItem *m_mainStage;
138 QQmlComponent *m_sideStageComponent;
139 QQuickItem *m_sideStage;
140
141 int m_rightMargin;
142126
143 static ApplicationManager *the_application_manager;127 static ApplicationManager *the_application_manager;
144};128};
145129
=== modified file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp'
--- tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-04-02 15:33:33 +0000
+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-07-28 15:50:22 +0000
@@ -64,7 +64,7 @@
64 Q_FOREACH (QWindow *win, unity->allWindows()) {64 Q_FOREACH (QWindow *win, unity->allWindows()) {
65 QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);65 QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);
66 if (quickWin) {66 if (quickWin) {
67 image = image.scaledToWidth(quickWin->width() - m_appManager->rightMargin());67 image = image.scaledToWidth(quickWin->width());
68 break;68 break;
69 }69 }
70 }70 }
7171
=== modified file 'tests/mocks/Unity/CMakeLists.txt'
--- tests/mocks/Unity/CMakeLists.txt 2014-07-08 09:23:14 +0000
+++ tests/mocks/Unity/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -2,6 +2,7 @@
2add_subdirectory(Indicators)2add_subdirectory(Indicators)
3add_subdirectory(Launcher)3add_subdirectory(Launcher)
4add_subdirectory(Notifications)4add_subdirectory(Notifications)
5add_subdirectory(DashCommunicator)
56
6pkg_search_module(DEE dee-1.0 REQUIRED)7pkg_search_module(DEE dee-1.0 REQUIRED)
7pkg_search_module(GOBJECT gobject-2.0 REQUIRED)8pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
89
=== added directory 'tests/mocks/Unity/DashCommunicator'
=== added file 'tests/mocks/Unity/DashCommunicator/CMakeLists.txt'
--- tests/mocks/Unity/DashCommunicator/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/CMakeLists.txt 2014-07-28 15:50:22 +0000
@@ -0,0 +1,18 @@
1include_directories(
2 ${CMAKE_CURRENT_SOURCE_DIR}
3)
4
5set(QMLDASHCOMMUNICATORPLUGIN_SRC
6 plugin.cpp
7 dashcommunicatorservice.cpp
8 dashcommunicator.cpp
9 )
10
11add_library(MockDashCommunicator-qml MODULE
12 ${QMLDASHCOMMUNICATORPLUGIN_SRC}
13 )
14
15qt5_use_modules(MockDashCommunicator-qml DBus Qml)
16
17# export the qmldir and qmltypes files
18add_unity8_mock(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS MockDashCommunicator-qml)
019
=== added file 'tests/mocks/Unity/DashCommunicator/dashcommunicator.cpp'
--- tests/mocks/Unity/DashCommunicator/dashcommunicator.cpp 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/dashcommunicator.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "dashcommunicator.h"
18
19#include <QObject>
20#include <QDBusConnection>
21#include <QDBusInterface>
22#include <QDebug>
23
24DashCommunicator::DashCommunicator(QObject *parent):
25 QObject(parent)
26{
27}
28
29DashCommunicator::~DashCommunicator()
30{
31}
32
33
34void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool reset)
35{
36 Q_EMIT setCurrentScopeCalled(scopeId, animate, reset);
37}
038
=== added file 'tests/mocks/Unity/DashCommunicator/dashcommunicator.h'
--- tests/mocks/Unity/DashCommunicator/dashcommunicator.h 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/dashcommunicator.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DASHCOMMUNICATOR_H
18#define DASHCOMMUNICATOR_H
19
20#include <QObject>
21
22class DashCommunicator: public QObject
23{
24 Q_OBJECT
25 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26
27public:
28 DashCommunicator(QObject *parent = 0);
29 ~DashCommunicator();
30
31public Q_SLOTS:
32 void setCurrentScope(const QString &scopeId, bool animate, bool reset);
33
34Q_SIGNALS:
35 // This mock just emits calls back to the QML api for the plugin to verify calls
36 void setCurrentScopeCalled(const QString &scopeId, bool animate, bool reset);
37};
38
39#endif
040
=== added file 'tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cpp'
--- tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cpp 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,33 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "dashcommunicatorservice.h"
18
19DashCommunicatorService::DashCommunicatorService(QObject *parent):
20 QObject(parent)
21{
22}
23
24
25DashCommunicatorService::~DashCommunicatorService()
26{
27
28}
29
30void DashCommunicatorService::mockSetCurrentScope(const QString &scopeId, bool animate, bool reset)
31{
32 Q_EMIT setCurrentScope(scopeId, animate, reset);
33}
034
=== added file 'tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.h'
--- tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.h 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,36 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DASHCOMMUNICATORSERVICE_H
18#define DASHCOMMUNICATORSERVICE_H
19
20#include <QObject>
21
22class DashCommunicatorService: public QObject
23{
24 Q_OBJECT
25public:
26 DashCommunicatorService(QObject *parent = 0);
27 ~DashCommunicatorService();
28
29Q_SIGNALS:
30 void setCurrentScope(const QString &scopeId, bool animate, bool reset);
31
32public Q_SLOTS:
33 void mockSetCurrentScope(const QString &scopeId, bool animate, bool reset);
34};
35
36#endif // DBUSUNITYSESSIONSERVICE_H
037
=== added file 'tests/mocks/Unity/DashCommunicator/plugin.cpp'
--- tests/mocks/Unity/DashCommunicator/plugin.cpp 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/plugin.cpp 2014-07-28 15:50:22 +0000
@@ -0,0 +1,31 @@
1/*
2 * Copyright (C) 2014 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 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */
18
19#include "plugin.h"
20#include "dashcommunicator.h"
21#include "dashcommunicatorservice.h"
22
23#include <QDBusConnection>
24#include <QtQml/qqml.h>
25
26void DashCommunicatorPlugin::registerTypes(const char *uri)
27{
28 Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
29 qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
30 qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
31}
032
=== added file 'tests/mocks/Unity/DashCommunicator/plugin.h'
--- tests/mocks/Unity/DashCommunicator/plugin.h 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/plugin.h 2014-07-28 15:50:22 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2014 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 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */
18
19#ifndef DASHCOMMUNICATOR_PLUGIN_H
20#define DASHCOMMUNICATOR_PLUGIN_H
21
22#include <QtQml/QQmlEngine>
23#include <QtQml/QQmlExtensionPlugin>
24
25class DashCommunicatorPlugin : public QQmlExtensionPlugin
26{
27 Q_OBJECT
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
30public:
31 void registerTypes(const char *uri);
32};
33
34#endif
035
=== added file 'tests/mocks/Unity/DashCommunicator/qmldir'
--- tests/mocks/Unity/DashCommunicator/qmldir 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/DashCommunicator/qmldir 2014-07-28 15:50:22 +0000
@@ -0,0 +1,3 @@
1module Unity.DashCommunicator
2plugin MockDashCommunicator-qml
3typeinfo DashCommunicator.qmltypes
04
=== modified file 'tests/qmltests/Dash/tst_Dash.qml'
--- tests/qmltests/Dash/tst_Dash.qml 2014-07-11 11:45:45 +0000
+++ tests/qmltests/Dash/tst_Dash.qml 2014-07-28 15:50:22 +0000
@@ -59,6 +59,7 @@
59 verify(dashContentList != undefined);59 verify(dashContentList != undefined);
60 tryCompare(dashContentList, "count", 0);60 tryCompare(dashContentList, "count", 0);
61 scopes.load();61 scopes.load();
62 tryCompare(dashContentList, "currentIndex", 0);
62 }63 }
6364
64 function get_scope_data() {65 function get_scope_data() {
@@ -91,5 +92,23 @@
91 verify(dashContentList != undefined);92 verify(dashContentList != undefined);
92 tryCompare(dashContentList, "currentIndex", data.visualIndex);93 tryCompare(dashContentList, "currentIndex", data.visualIndex);
93 }94 }
95
96 function test_setCurrentScope() {
97 var dashContentList = findChild(dash, "dashContentList");
98 var startX = dash.width - units.gu(1);
99 var startY = dash.height / 2;
100 var stopX = units.gu(1)
101 var stopY = startY;
102 var retry = 0;
103 while (dashContentList.currentIndex != 2 && retry <= 5) {
104 mouseFlick(dash, startX, startY, stopX, stopY)
105 waitForRendering(dashContentList)
106 retry++;
107 }
108 compare(dashContentList.currentIndex, 2);
109 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
110 dashCommunicatorService.mockSetCurrentScope("clickscope", true, true);
111 tryCompare(dashContentList, "currentIndex", 1)
112 }
94 }113 }
95}114}
96115
=== modified file 'tests/qmltests/Stages/tst_PhoneStage.qml'
--- tests/qmltests/Stages/tst_PhoneStage.qml 2014-07-28 15:50:19 +0000
+++ tests/qmltests/Stages/tst_PhoneStage.qml 2014-07-28 15:50:22 +0000
@@ -231,9 +231,10 @@
231 }231 }
232232
233 function cleanup() {233 function cleanup() {
234 while (ApplicationManager.count > 0) {234 while (ApplicationManager.count > 1) {
235 var oldCount = ApplicationManager.count;235 var oldCount = ApplicationManager.count;
236 ApplicationManager.stopApplication(ApplicationManager.get(0).appId)236 var closingIndex = ApplicationManager.focusedApplicationId == "unity8-dash" ? 1 : 0
237 ApplicationManager.stopApplication(ApplicationManager.get(closingIndex).appId)
237 tryCompare(ApplicationManager, "count", oldCount - 1)238 tryCompare(ApplicationManager, "count", oldCount - 1)
238 }239 }
239 }240 }
240241
=== modified file 'tests/qmltests/tst_Shell.qml'
--- tests/qmltests/tst_Shell.qml 2014-07-28 15:50:19 +0000
+++ tests/qmltests/tst_Shell.qml 2014-07-28 15:50:22 +0000
@@ -56,49 +56,20 @@
56 signalName: "sessionStarted"56 signalName: "sessionStarted"
57 }57 }
5858
59 SignalSpy {
60 id: dashCommunicatorSpy
61 signalName: "setCurrentScopeCalled"
62 }
63
59 UT.UnityTestCase {64 UT.UnityTestCase {
60 name: "Shell"65 name: "Shell"
61 when: windowShown66 when: windowShown
6267
63 function initTestCase() {68 function initTestCase() {
64 var ok = false;
65 var attempts = 0;
66 var maxAttempts = 1000;
67
68 // Qt loads a qml scene asynchronously. So early on, some findChild() calls made in
69 // tests may fail because the desired child item wasn't loaded yet.
70 // Thus here we try to ensure the scene has been fully loaded before proceeding with the tests.
71 // As I couldn't find an API in QQuickView & friends to tell me that the scene is 100% loaded
72 // (all items instantiated, etc), I resort to checking the existence of some key items until
73 // repeatedly until they're all there.
74 do {
75 var dashContentList = findChild(shell, "dashContentList");
76 waitForRendering(dashContentList);
77 var homeLoader = findChild(dashContentList, "clickscope loader");
78 ok = homeLoader !== null
79 && homeLoader.item !== undefined;
80
81 var greeter = findChild(shell, "greeter");
82 ok &= greeter !== null;
83
84 var launcherPanel = findChild(shell, "launcherPanel");
85 ok &= launcherPanel !== null;
86
87 attempts++;
88 if (!ok) {
89 console.log("Attempt " + attempts + " failed. Waiting a bit before trying again.");
90 // wait a bit before retrying
91 wait(100);
92 } else {
93 console.log("All seem fine after " + attempts + " attempts.");
94 }
95 } while (!ok && attempts <= maxAttempts);
96
97 verify(ok);
98
99 swipeAwayGreeter();69 swipeAwayGreeter();
10070
101 sessionSpy.target = findChild(shell, "greeter")71 sessionSpy.target = findChild(shell, "greeter")
72 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator");
102 }73 }
10374
104 function cleanup() {75 function cleanup() {
@@ -111,56 +82,16 @@
111 // kill all (fake) running apps82 // kill all (fake) running apps
112 killApps(ApplicationManager);83 killApps(ApplicationManager);
11384
114 var dashContent = findChild(shell, "dashContent");85 waitForUIToSettle();
115 dashContent.closePreview();
116
117 var dashHome = findChild(shell, "clickscope loader");
118 swipeUntilScopeViewIsReached(dashHome);
119
120 hideIndicators();86 hideIndicators();
121 }87 }
12288
123 function killApps(apps) {89 function killApps() {
124 if (!apps) return;90 while (ApplicationManager.count > 1) {
125 while (apps.count > 0) {91 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
126 ApplicationManager.stopApplication(apps.get(0).appId);92 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
127 }93 }
128 compare(ApplicationManager.count, 0)94 compare(ApplicationManager.count, 1)
129 }
130
131 /*
132 Test the effect of a right-edge drag on the dash in 3 situations:
133 1 - when no application has been launched yet
134 2 - when there's a minimized application
135 3 - after the last running application has been closed/stopped
136
137 The behavior of Dash on 3 should be the same as on 1.
138 */
139 function test_rightEdgeDrag() {
140 checkRightEdgeDragWithNoRunningApps();
141
142 dragLauncherIntoView();
143
144 // Launch an app from the launcher
145 tapOnAppIconInLauncher();
146 waitUntilApplicationWindowIsFullyVisible();
147
148 // Minimize the application we just launched
149 swipeFromLeftEdge(units.gu(27));
150
151 waitForUIToSettle();
152
153 checkRightEdgeDragWithMinimizedApp();
154
155 // Minimize that application once again
156 swipeFromLeftEdge(units.gu(27));
157
158 // Right edge behavior should now be the same as before that app,
159 // was launched. Manually cleanup beforehand to get to initial
160 // state.
161 cleanup();
162 waitForUIToSettle();
163 checkRightEdgeDragWithNoRunningApps();
164 }95 }
16596
166 function test_leftEdgeDrag_data() {97 function test_leftEdgeDrag_data() {
@@ -183,7 +114,7 @@
183114
184 swipeFromLeftEdge(data.swipeLength);115 swipeFromLeftEdge(data.swipeLength);
185 if (data.appHides)116 if (data.appHides)
186 waitUntilApplicationWindowIsFullyHidden();117 waitUntilDashIsFocused();
187 else118 else
188 waitUntilApplicationWindowIsFullyVisible();119 waitUntilApplicationWindowIsFullyVisible();
189 }120 }
@@ -240,99 +171,17 @@
240 }171 }
241172
242 /*173 /*
243 Perform a right-edge drag when the Dash is being show and there are
244 no running/minimized apps to be restored.
245
246 The expected behavior is that an animation should be played to hint the
247 user that his right-edge drag gesture has been successfully recognized
248 but there is no application to be brought to foreground.
249 */
250 function checkRightEdgeDragWithNoRunningApps() {
251 var touchX = shell.width - (shell.edgeSize / 2);
252 var touchY = shell.height / 2;
253
254 var dash = findChild(shell, "dash");
255 // check that dash has normal scale and opacity
256 tryCompare(dash, "contentScale", 1.0);
257 tryCompare(dash, "opacity", 1.0);
258
259 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
260 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
261
262 // check that Dash has been scaled down and had its opacity reduced
263 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
264 tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
265
266 touchRelease(shell, shell.width * 0.1, touchY);
267
268 // and now everything should have gone back to normal
269 tryCompare(dash, "contentScale", 1.0);
270 tryCompare(dash, "opacity", 1.0);
271 }
272
273 /*
274 Perform a right-edge drag when the Dash is being show and there is
275 a running/minimized app to be restored.
276
277 The expected behavior is that the dash should fade away and ultimately be
278 made invisible once the gesture is finished as the restored app will now
279 be on foreground.
280 */
281 function checkRightEdgeDragWithMinimizedApp() {
282 var touchX = shell.width - (shell.edgeSize / 2);
283 var touchY = shell.height / 2;
284
285 var dash = findChild(shell, "dash");
286 // check that dash has normal scale and opacity
287 tryCompare(dash, "contentScale", 1.0);
288 tryCompare(dash, "opacity", 1.0);
289
290 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
291 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
292
293 // check that Dash has been scaled down and had its opacity reduced
294 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
295 tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
296
297 touchRelease(shell, shell.width * 0.1, touchY);
298
299 // dash should have gone away, now that the app is on foreground
300 tryCompare(dash, "visible", false);
301 }
302
303 /*
304 Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419174 Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
305175
306 When the user minimizes an application (left-edge swipe) he should always end up in the "Running Apps"176 When the user minimizes an application (left-edge swipe) he should always end up in the
307 category of the "Applications" scope view.177 "Applications" scope view.
308178
309 Steps:179 Steps:
310 - go to apps lens180 - reveal launcher and launch an app that covers the dash
311 - scroll to the bottom
312 - reveal launcher and launch an app
313 - perform long left edge swipe to go minimize the app and go back to the dash.181 - perform long left edge swipe to go minimize the app and go back to the dash.
314182 - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
315 Expected Results
316 - apps lens shown
317 */183 */
318 function test_minimizingAppTakesToDashApps() {184 function test_minimizingAppTakesToDashApps() {
319 var dashApps = findChild(shell, "clickscope");
320 swipeUntilScopeViewIsReached(dashApps);
321
322 // swipe finger up until the running/recent apps section (which we assume
323 // it's the first one) is as far from view as possible.
324 // We also assume that DashApps is tall enough that it's scrollable
325 var appsCategoryListView = findChild(dashApps, "categoryListView");
326 while (!appsCategoryListView.atYEnd) {
327 swipeUpFromCenter();
328 tryCompare(appsCategoryListView, "moving", false);
329 }
330
331 // Switch away from the Applications scope.
332 swipeRightFromCenter();
333 waitUntilItemStopsMoving(dashApps);
334 verify(!itemIsOnScreen(dashApps));
335
336 dragLauncherIntoView();185 dragLauncherIntoView();
337186
338 // Launch an app from the launcher187 // Launch an app from the launcher
@@ -340,19 +189,16 @@
340189
341 waitUntilApplicationWindowIsFullyVisible();190 waitUntilApplicationWindowIsFullyVisible();
342191
343 // Dragging launcher into view with a little bit of gap (units.gu(1)) should switch to Apps scope192 verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
344 dragLauncherIntoView();
345 verify(itemIsOnScreen(dashApps));
346193
194 dashCommunicatorSpy.clear();
347 // Minimize the application we just launched195 // Minimize the application we just launched
348 swipeFromLeftEdge(units.gu(27));196 swipeFromLeftEdge(units.gu(27));
349197
350 // Wait for the whole UI to settle down198 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
351 waitUntilApplicationWindowIsFullyHidden();
352 waitUntilItemStopsMoving(dashApps);
353 tryCompare(appsCategoryListView, "moving", false);
354199
355 verify(itemIsOnScreen(dashApps));200 compare(dashCommunicatorSpy.count, 1);
201 compare(dashCommunicatorSpy.signalArguments[0][0], "clickscope");
356 }202 }
357203
358 function test_showInputMethod() {204 function test_showInputMethod() {
@@ -409,9 +255,14 @@
409255
410 // Wait for the whole UI to settle down256 // Wait for the whole UI to settle down
411 function waitForUIToSettle() {257 function waitForUIToSettle() {
412 waitUntilApplicationWindowIsFullyHidden();258 var launcher = findChild(shell, "launcherPanel")
413 var dashContentList = findChild(shell, "dashContentList");259 tryCompareFunction(function() {return launcher.x === 0 || launcher.x === -launcher.width;}, true);
414 tryCompare(dashContentList, "moving", false);260 if (launcher.x === 0) {
261 mouseClick(shell, shell.width / 2, shell.height / 2)
262 }
263 tryCompare(launcher, "x", -launcher.width)
264
265 waitForRendering(shell)
415 }266 }
416267
417 function dragToCloseIndicatorsPanel() {268 function dragToCloseIndicatorsPanel() {
@@ -468,24 +319,13 @@
468 }319 }
469320
470 function waitUntilApplicationWindowIsFullyVisible() {321 function waitUntilApplicationWindowIsFullyVisible() {
471 var underlay = findChild(shell, "underlay");322 var appDelegate = findChild(shell, "appDelegate0")
472 tryCompare(underlay, "visible", false);323 var surfaceContainer = findChild(appDelegate, "surfaceContainer");
473 }324 tryCompareFunction(function() { return surfaceContainer.surface !== null; }, true);
474325 }
475 function waitUntilApplicationWindowIsFullyHidden() {326
476 var stages = findChild(shell, "stages");327 function waitUntilDashIsFocused() {
477 tryCompare(stages, "fullyHidden", true);328 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
478 }
479
480 function swipeUntilScopeViewIsReached(scopeView) {
481 while (!itemIsOnScreen(scopeView)) {
482 if (itemIsToLeftOfScreen(scopeView)) {
483 swipeRightFromCenter();
484 } else {
485 swipeLeftFromCenter();
486 }
487 waitUntilItemStopsMoving(scopeView);
488 }
489 }329 }
490330
491 function swipeFromLeftEdge(swipeLength) {331 function swipeFromLeftEdge(swipeLength) {
@@ -494,24 +334,6 @@
494 touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);334 touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
495 }335 }
496336
497 function swipeLeftFromCenter() {
498 var touchStartX = shell.width * 3 / 4;
499 var touchStartY = shell.height / 2;
500 touchFlick(shell, touchStartX, touchStartY, 0, touchStartY);
501 }
502
503 function swipeRightFromCenter() {
504 var touchStartX = shell.width * 3 / 4;
505 var touchStartY = shell.height / 2;
506 touchFlick(shell, touchStartX, touchStartY, shell.width, touchStartY);
507 }
508
509 function swipeUpFromCenter() {
510 var touchStartX = shell.width / 2;
511 var touchStartY = shell.height / 2;
512 touchFlick(shell, touchStartX, touchStartY, touchStartX, 0);
513 }
514
515 function itemIsOnScreen(item) {337 function itemIsOnScreen(item) {
516 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);338 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
517339
@@ -521,64 +343,6 @@
521 && itemRectInShell.y + itemRectInShell.height <= shell.height;343 && itemRectInShell.y + itemRectInShell.height <= shell.height;
522 }344 }
523345
524 function itemIsToLeftOfScreen(item) {
525 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
526 return itemRectInShell.x < 0;
527 }
528
529 function waitUntilItemStopsMoving(item) {
530 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
531 var previousX = itemRectInShell.x;
532 var previousY = itemRectInShell.y;
533 var isStill = false;
534
535 do {
536 wait(100);
537 itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
538 if (itemRectInShell.x == previousX && itemRectInShell.y == previousY) {
539 isStill = true;
540 } else {
541 previousX = itemRectInShell.x;
542 previousY = itemRectInShell.y;
543 }
544 } while (!isStill);
545 }
546
547 function test_DashShown_data() {
548 return [
549 {tag: "in focus", greeter: false, app: false, launcher: false, indicators: false, expectedShown: true},
550 {tag: "under greeter", greeter: true, app: false, launcher: false, indicators: false, expectedShown: false},
551 {tag: "under app", greeter: false, app: true, launcher: false, indicators: false, expectedShown: false},
552 {tag: "under launcher", greeter: false, app: false, launcher: true, indicators: false, expectedShown: true},
553 {tag: "under indicators", greeter: false, app: false, launcher: false, indicators: true, expectedShown: false},
554 ]
555 }
556
557 function test_DashShown(data) {
558 if (data.greeter) {
559 // Swipe the greeter in
560 var greeter = findChild(shell, "greeter");
561 Powerd.displayPowerStateChange(Powerd.Off, 0);
562 tryCompare(greeter, "showProgress", 1);
563 }
564
565 if (data.app) {
566 dragLauncherIntoView();
567 tapOnAppIconInLauncher();
568 }
569
570 if (data.launcher) {
571 dragLauncherIntoView();
572 }
573
574 if (data.indicators) {
575 showIndicators();
576 }
577
578 var dash = findChild(shell, "dash");
579 tryCompare(dash, "shown", data.expectedShown);
580 }
581
582 function test_focusRequestedHidesGreeter() {346 function test_focusRequestedHidesGreeter() {
583 var greeter = findChild(shell, "greeter");347 var greeter = findChild(shell, "greeter");
584348
@@ -587,10 +351,9 @@
587 tryCompareFunction(function() { return app.surface != null }, true);351 tryCompareFunction(function() { return app.surface != null }, true);
588352
589 // Minimize the application we just launched353 // Minimize the application we just launched
590 swipeFromLeftEdge(units.gu(27));354 swipeFromLeftEdge(units.gu(26) + 1);
591355
592 // Wait for the whole UI to settle down356 waitUntilDashIsFocused();
593 waitUntilApplicationWindowIsFullyHidden();
594357
595 greeter.show();358 greeter.show();
596 tryCompare(greeter, "showProgress", 1);359 tryCompare(greeter, "showProgress", 1);
@@ -606,8 +369,9 @@
606369
607 showIndicators();370 showIndicators();
608371
372 var oldCount = ApplicationManager.count;
609 ApplicationManager.startApplication("camera-app");373 ApplicationManager.startApplication("camera-app");
610 tryCompare(ApplicationManager, "count", 1);374 tryCompare(ApplicationManager, "count", oldCount + 1);
611375
612 tryCompare(indicators, "fullyClosed", true);376 tryCompare(indicators, "fullyClosed", true);
613 }377 }
@@ -616,6 +380,7 @@
616 function test_showGreeterDBusCall() {380 function test_showGreeterDBusCall() {
617 var greeter = findChild(shell, "greeter")381 var greeter = findChild(shell, "greeter")
618 tryCompare(greeter, "showProgress", 0)382 tryCompare(greeter, "showProgress", 0)
383 waitForRendering(greeter);
619 LightDM.Greeter.showGreeter()384 LightDM.Greeter.showGreeter()
620 tryCompare(greeter, "showProgress", 1)385 tryCompare(greeter, "showProgress", 1)
621 }386 }
@@ -624,6 +389,7 @@
624 sessionSpy.clear()389 sessionSpy.clear()
625390
626 var greeter = findChild(shell, "greeter")391 var greeter = findChild(shell, "greeter")
392 waitForRendering(greeter)
627 greeter.show()393 greeter.show()
628 tryCompare(greeter, "showProgress", 1)394 tryCompare(greeter, "showProgress", 1)
629395
630396
=== modified file 'tests/qmltests/tst_ShellWithPin.qml'
--- tests/qmltests/tst_ShellWithPin.qml 2014-07-15 18:26:25 +0000
+++ tests/qmltests/tst_ShellWithPin.qml 2014-07-28 15:50:22 +0000
@@ -58,40 +58,6 @@
58 when: windowShown58 when: windowShown
5959
60 function initTestCase() {60 function initTestCase() {
61 var ok = false;
62 var attempts = 0;
63 var maxAttempts = 1000;
64
65 // Qt loads a qml scene asynchronously. So early on, some findChild() calls made in
66 // tests may fail because the desired child item wasn't loaded yet.
67 // Thus here we try to ensure the scene has been fully loaded before proceeding with the tests.
68 // As I couldn't find an API in QQuickView & friends to tell me that the scene is 100% loaded
69 // (all items instantiated, etc), I resort to checking the existence of some key items until
70 // repeatedly until they're all there.
71 do {
72 var dashContentList = findChild(shell, "dashContentList");
73 waitForRendering(dashContentList);
74 var homeLoader = findChild(dashContentList, "clickscope loader");
75 ok = homeLoader !== null
76 && homeLoader.item !== undefined;
77
78 var greeter = findChild(shell, "greeter");
79 ok &= greeter !== null;
80
81 var launcherPanel = findChild(shell, "launcherPanel");
82 ok &= launcherPanel !== null;
83
84 attempts++;
85 if (!ok) {
86 console.log("Attempt " + attempts + " failed. Waiting a bit before trying again.");
87 // wait a bit before retrying
88 wait(100);
89 } else {
90 console.log("All seem fine after " + attempts + " attempts.");
91 }
92 } while (!ok && attempts <= maxAttempts);
93
94 verify(ok);
9561
96 sessionSpy.target = findChild(shell, "greeter")62 sessionSpy.target = findChild(shell, "greeter")
97 }63 }
@@ -110,10 +76,11 @@
110 }76 }
11177
112 function killApps() {78 function killApps() {
113 while (ApplicationManager.count > 0) {79 while (ApplicationManager.count > 1) {
114 ApplicationManager.stopApplication(ApplicationManager.get(0).appId)80 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
81 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
115 }82 }
116 compare(ApplicationManager.count, 0)83 compare(ApplicationManager.count, 1)
117 }84 }
11885
119 function swipeAwayGreeter() {86 function swipeAwayGreeter() {

Subscribers

People subscribed via source and target branches