Merge lp:~macslow/unity8/shellRotation into lp:unity8

Proposed by Michał Sawicz
Status: Superseded
Proposed branch: lp:~macslow/unity8/shellRotation
Merge into: lp:unity8
Diff against target: 7643 lines (+4989/-658)
80 files modified
CMakeLists.txt (+2/-0)
data/unity8-dash.desktop.in (+1/-0)
debian/control (+5/-4)
debian/unity8.install (+3/-0)
plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Utils/CMakeLists.txt (+1/-0)
plugins/Utils/plugin.cpp (+5/-2)
plugins/Utils/windowscreenshotprovider.cpp (+59/-0)
plugins/Utils/windowscreenshotprovider.h (+31/-0)
qml/CMakeLists.txt (+1/-0)
qml/Components/WindowScreenshot.qml (+35/-0)
qml/Dash/graphics/phone/screenshots/gmail-webapp.svg (+343/-0)
qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svg (+201/-0)
qml/DeviceConfiguration.qml (+81/-0)
qml/Greeter/Greeter.qml (+2/-0)
qml/Greeter/NarrowView.qml (+1/-0)
qml/Greeter/WideView.qml (+1/-0)
qml/OrientedShell.qml (+182/-0)
qml/Panel/Panel.qml (+9/-0)
qml/Rotation/HalfLoopRotationAnimation.qml (+46/-0)
qml/Rotation/ImmediateRotationAction.qml (+45/-0)
qml/Rotation/NinetyRotationAnimation.qml (+90/-0)
qml/Rotation/RotationStates.qml (+271/-0)
qml/Shell.qml (+110/-49)
qml/Stages/ApplicationWindow.qml (+53/-18)
qml/Stages/DesktopStage.qml (+23/-0)
qml/Stages/OrientationChangeAnimation.qml (+243/-0)
qml/Stages/PhoneStage.qml (+141/-41)
qml/Stages/SessionContainer.qml (+2/-8)
qml/Stages/SpreadDelegate.qml (+242/-25)
qml/Stages/SurfaceContainer.qml (+2/-2)
qml/Stages/TabletStage.qml (+154/-20)
qml/Stages/TransformedTabletSpreadDelegate.qml (+5/-2)
run.sh (+1/-1)
src/ApplicationArguments.cpp (+22/-0)
src/ApplicationArguments.h (+10/-6)
src/CMakeLists.txt (+7/-7)
src/Dash/CMakeLists.txt (+7/-1)
src/UnityCommandLineParser.cpp (+123/-0)
src/UnityCommandLineParser.h (+52/-0)
src/main.cpp (+20/-70)
tests/autopilot/unity8/fixture_setup.py (+129/-1)
tests/autopilot/unity8/indicators/tests/test_display_indicator.py (+6/-0)
tests/autopilot/unity8/process_helpers.py (+2/-2)
tests/autopilot/unity8/sensors.py (+90/-0)
tests/autopilot/unity8/shell/emulators/main_window.py (+12/-0)
tests/autopilot/unity8/shell/tests/__init__.py (+2/-0)
tests/autopilot/unity8/shell/tests/test_rotation.py (+134/-0)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+45/-4)
tests/mocks/Unity/Application/ApplicationInfo.h (+9/-1)
tests/mocks/Unity/Application/ApplicationManager.cpp (+39/-2)
tests/mocks/Unity/Application/CMakeLists.txt (+4/-1)
tests/mocks/Unity/Application/MirSurfaceItem.cpp (+12/-29)
tests/mocks/Unity/Application/MirSurfaceItem.h (+29/-29)
tests/mocks/Unity/Application/MirSurfaceItem.qml (+12/-14)
tests/mocks/Unity/Application/SurfaceManager.cpp (+4/-34)
tests/mocks/Unity/Application/SurfaceManager.h (+2/-5)
tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp (+33/-1)
tests/mocks/Unity/Application/UbuntuKeyboardInfo.h (+11/-5)
tests/mocks/Unity/Application/VirtualKeyboard.cpp (+44/-0)
tests/mocks/Unity/Application/VirtualKeyboard.h (+42/-0)
tests/mocks/Unity/Application/VirtualKeyboard.qml (+7/-1)
tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+3/-1)
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+3/-1)
tests/qmltests/CMakeLists.txt (+1/-0)
tests/qmltests/Panel/tst_IndicatorsMenu.qml (+0/-2)
tests/qmltests/Panel/tst_Panel.qml (+0/-2)
tests/qmltests/Stages/tst_ApplicationWindow.qml (+14/-23)
tests/qmltests/Stages/tst_PhoneStage.qml (+5/-58)
tests/qmltests/Stages/tst_SessionContainer.qml (+1/-64)
tests/qmltests/Stages/tst_SpreadDelegate.qml (+173/-38)
tests/qmltests/Stages/tst_TabletStage.qml (+51/-0)
tests/qmltests/tst_OrientedShell.qml (+1097/-0)
tests/qmltests/tst_Shell.qml (+15/-69)
tests/qmltests/tst_ShellWithPin.qml (+6/-10)
tests/qmltests/tst_TabletShell.qml (+305/-0)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+11/-1)
To merge this branch: bzr merge lp:~macslow/unity8/shellRotation
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+255667@code.launchpad.net

This proposal has been superseded by a proposal from 2015-04-16.

Commit message

Implemented autopilot-test and fake-sensors for shell-rotation.

Description of the change

Implemented autopilot-test and fake-sensors for shell-rotation.

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

The branch lp:~dandrader/qtmir/supportedOrientations needs to be merged to lp:qtmir first before this can work properly.

For testing/reviewer convenience use ppa:unity-team/demo-stuff to get the needed package dependencies in place. In addition to that, wipe your ~/.cache/QML directory on the device before running shell-rotation autopilot-test (see https://bugs.launchpad.net/ubuntu/+source/qtdeclarative-opensource-src/+bug/1444937).

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

* 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?
Not applicable.

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

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

I aborted the CI run as it was bound to fail without the dependencies anyway...

lp:~macslow/unity8/shellRotation updated
1597. By Mirco Müller

Removed sneaky bzr-cruft.

1598. By Mirco Müller

Merged with ~unity-team/unity8/shellRotation and fixed conflicts.

1599. By Mirco Müller

Merged with unity-team's shellRotation branch and fixed conflicts.

1600. By Mirco Müller

Use the logger-facility for spitting out runtime-info about the test.

1601. By Mirco Müller

Make code a bit more tight.

1602. By Mirco Müller

Removed tst_TabletShell.qml added by mistake earlier.

1603. By Mirco Müller

Fix some issues from MP-comments... use scenarios as much as possible to make ap-test more compact.

1604. By Mirco Müller

More Python3-ification according to MP-comments.

1605. By Mirco Müller

Forgot import

1606. By Mirco Müller

flake8-ify shell-rotation ap-test.

1607. By Mirco Müller

Merged with unity8 trunk.

1608. By Mirco Müller

Build-dependency on libunity-api-dev >= 7.97 is wrong 7.96 is sufficient.

1609. By Mirco Müller

Argl

1610. By Mirco Müller

Removed to lines that must have been added by a bogus branch-merge. The display-indicator ap-tests were never meant to be touched.

1611. By Mirco Müller

Make test skip earlier and save execution-time, if device does not support a certain orientation.

1612. By Mirco Müller

Merge with parent

1613. By Mirco Müller

Fix more flake8 errors.

1614. By Mirco Müller

Made get_unity_pid() available across ap-tests.

1615. By Mirco Müller

This should solve the too early skipping of the rotation ap-test.

1616. By Mirco Müller

To make the faked 'right-up'-orientation be recognized more consistently by qtubuntu-sensors, more granularity steps were needed.

1617. By Mirco Müller

Merged with parent branch again and fixed conflicts.

1618. By Mirco Müller

Make sure to unset QV4_ENABLE_JIT_CACHE environment-variable for autopilot-test runs, due to LP: #1444937.

1619. By Mirco Müller

Revert changes from r1618 since the qml-cache-related issue can't be reproduced on other setups.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-03-12 13:59:07 +0000
3+++ CMakeLists.txt 2015-04-16 12:11:39 +0000
4@@ -56,6 +56,8 @@
5 find_package(Qt5Concurrent 5.2 REQUIRED)
6 find_package(Qt5Sql 5.2 REQUIRED)
7
8+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
9+
10 # Standard install paths
11 include(GNUInstallDirs)
12
13
14=== modified file 'data/unity8-dash.desktop.in'
15--- data/unity8-dash.desktop.in 2014-09-04 13:23:09 +0000
16+++ data/unity8-dash.desktop.in 2015-04-16 12:11:39 +0000
17@@ -7,3 +7,4 @@
18 Icon=
19 NoDisplay=true
20 X-Ubuntu-Touch=true
21+X-Ubuntu-Supported-Orientations=primary
22
23=== modified file 'debian/control'
24--- debian/control 2015-04-02 13:09:18 +0000
25+++ debian/control 2015-04-16 12:11:39 +0000
26@@ -14,6 +14,7 @@
27 # append :native to g++-4.9 so we don't try to run armhf g++
28 # on an x86 CPU for eaxmple, when cross-compiling.
29 g++-4.9:native,
30+ libandroid-properties-dev,
31 libconnectivity-qt1-dev,
32 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
33 libgl1-mesa-dri,
34@@ -27,7 +28,7 @@
35 libqmenumodel-dev (>= 0.2.9),
36 libqt5xmlpatterns5-dev,
37 libsystemsettings-dev,
38- libunity-api-dev (>= 7.96),
39+ libunity-api-dev (>= 7.97),
40 libusermetricsoutput1-dev,
41 libxcb1-dev,
42 pkg-config,
43@@ -94,7 +95,7 @@
44 qml-module-qtquick-xmllistmodel,
45 qml-module-qtsysteminfo,
46 qtdeclarative5-gsettings1.0,
47- qtdeclarative5-qtmir-plugin (>= 0.4.4),
48+ qtdeclarative5-qtmir-plugin (>= 0.4.5),
49 qtdeclarative5-ubuntu-telephony0.1,
50 qtdeclarative5-ubuntu-web-plugin,
51 ubuntu-system-settings,
52@@ -124,7 +125,7 @@
53 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
54 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
55 ubuntu-thumbnailer-impl-0,
56- unity-application-impl-4,
57+ unity-application-impl-6,
58 unity-notifications-impl-3,
59 unity-plugin-scopes | unity-scopes-impl,
60 unity-scopes-impl-6,
61@@ -168,7 +169,7 @@
62 Depends: ${misc:Depends},
63 ${shlibs:Depends},
64 Provides: unity-application-impl,
65- unity-application-impl-4,
66+ unity-application-impl-6,
67 Description: Fake environment for running Unity 8 shell
68 Provides fake implementations of some QML modules used by Unity 8 shell
69 (e.g Ubuntu.Application) so that you can run it in a sandboxed environment.
70
71=== modified file 'debian/unity8.install'
72--- debian/unity8.install 2014-12-16 16:49:49 +0000
73+++ debian/unity8.install 2015-04-16 12:11:39 +0000
74@@ -8,6 +8,9 @@
75 usr/share/unity8/Greeter
76 usr/share/unity8/Launcher
77 usr/share/unity8/Panel
78+usr/share/unity8/Rotation
79+usr/share/unity8/DeviceConfiguration.qml
80+usr/share/unity8/OrientedShell.qml
81 usr/share/unity8/Shell.qml
82 usr/share/unity8/Stages
83 usr/share/unity8/Tutorial
84
85=== modified file 'plugins/Greeter/Unity/Launcher/CMakeLists.txt'
86--- plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-03-31 01:03:55 +0000
87+++ plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-04-16 12:11:39 +0000
88@@ -1,5 +1,5 @@
89 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
90-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
91+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
92 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
93
94 add_definitions(-DSM_BUSNAME=systemBus)
95
96=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
97--- plugins/Unity/Launcher/CMakeLists.txt 2015-02-11 14:02:16 +0000
98+++ plugins/Unity/Launcher/CMakeLists.txt 2015-04-16 12:11:39 +0000
99@@ -1,5 +1,5 @@
100 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
101-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
102+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
103 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
104
105 add_definitions(-DSM_BUSNAME=systemBus)
106
107=== modified file 'plugins/Utils/CMakeLists.txt'
108--- plugins/Utils/CMakeLists.txt 2015-04-02 10:32:43 +0000
109+++ plugins/Utils/CMakeLists.txt 2015-04-16 12:11:39 +0000
110@@ -16,6 +16,7 @@
111 timeformatter.cpp
112 unitymenumodelpaths.cpp
113 windowkeysfilter.cpp
114+ windowscreenshotprovider.cpp
115 easingcurve.cpp
116 windowstatestorage.cpp
117 plugin.cpp
118
119=== modified file 'plugins/Utils/plugin.cpp'
120--- plugins/Utils/plugin.cpp 2015-04-07 11:50:48 +0000
121+++ plugins/Utils/plugin.cpp 2015-04-16 12:11:39 +0000
122@@ -1,5 +1,5 @@
123 /*
124- * Copyright (C) 2012 Canonical, Ltd.
125+ * Copyright (C) 2012-2015 Canonical, Ltd.
126 *
127 * This program is free software; you can redistribute it and/or modify
128 * it under the terms of the GNU General Public License as published by
129@@ -26,6 +26,7 @@
130 #include "plugin.h"
131
132 // local
133+#include "easingcurve.h"
134 #include "inputwatcher.h"
135 #include "qlimitproxymodelqml.h"
136 #include "unitysortfilterproxymodelqml.h"
137@@ -33,7 +34,7 @@
138 #include "timeformatter.h"
139 #include "unitymenumodelpaths.h"
140 #include "windowkeysfilter.h"
141-#include "easingcurve.h"
142+#include "windowscreenshotprovider.h"
143 #include "windowstatestorage.h"
144 #include "constants.h"
145
146@@ -71,4 +72,6 @@
147 void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
148 {
149 QQmlExtensionPlugin::initializeEngine(engine, uri);
150+
151+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
152 }
153
154=== added file 'plugins/Utils/windowscreenshotprovider.cpp'
155--- plugins/Utils/windowscreenshotprovider.cpp 1970-01-01 00:00:00 +0000
156+++ plugins/Utils/windowscreenshotprovider.cpp 2015-04-16 12:11:39 +0000
157@@ -0,0 +1,59 @@
158+/*
159+ * Copyright (C) 2015 Canonical, Ltd.
160+ *
161+ * This program is free software; you can redistribute it and/or modify
162+ * it under the terms of the GNU General Public License as published by
163+ * the Free Software Foundation; version 3.
164+ *
165+ * This program is distributed in the hope that it will be useful,
166+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
167+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
168+ * GNU General Public License for more details.
169+ *
170+ * You should have received a copy of the GNU General Public License
171+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
172+ */
173+
174+#include "windowscreenshotprovider.h"
175+
176+#include <QGuiApplication>
177+#include <QQuickWindow>
178+
179+WindowScreenshotProvider::WindowScreenshotProvider()
180+ : QQuickImageProvider(QQmlImageProviderBase::Image, 0)
181+{
182+}
183+
184+// A very simple implementation where we assume that there's only one window and that it's a
185+// QQuickWindow. Thus the id parameter is irrelevant.
186+//
187+// Idea: Make the id contain the objectName of the QQuickWindow once we care about a multi-display
188+// compositor?
189+// Strictly speaking that could be the actual QWindow::winId(), but that's mostly a
190+// meaningless arbitrary number.
191+QImage WindowScreenshotProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
192+{
193+ Q_UNUSED(id);
194+ Q_UNUSED(requestedSize);
195+
196+ QWindowList windows = QGuiApplication::topLevelWindows();
197+
198+ if (windows.count() != 1) {
199+ size->rwidth() = 0;
200+ size->rheight() = 0;
201+ return QImage();
202+ }
203+
204+ QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(windows[0]);
205+
206+ if (!quickWindow) {
207+ size->rwidth() = 0;
208+ size->rheight() = 0;
209+ return QImage();
210+ }
211+
212+ QImage image = quickWindow->grabWindow();
213+ size->rwidth() = image.width();
214+ size->rheight() = image.height();
215+ return image;
216+}
217
218=== added file 'plugins/Utils/windowscreenshotprovider.h'
219--- plugins/Utils/windowscreenshotprovider.h 1970-01-01 00:00:00 +0000
220+++ plugins/Utils/windowscreenshotprovider.h 2015-04-16 12:11:39 +0000
221@@ -0,0 +1,31 @@
222+/*
223+ * Copyright (C) 2015 Canonical, Ltd.
224+ *
225+ * This program is free software; you can redistribute it and/or modify
226+ * it under the terms of the GNU General Public License as published by
227+ * the Free Software Foundation; version 3.
228+ *
229+ * This program is distributed in the hope that it will be useful,
230+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
231+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
232+ * GNU General Public License for more details.
233+ *
234+ * You should have received a copy of the GNU General Public License
235+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
236+ */
237+
238+#ifndef WINDOW_SCREENSHOT_PROVIDER_H_
239+#define WINDOW_SCREENSHOT_PROVIDER_H_
240+
241+#include <QQuickImageProvider>
242+
243+class WindowScreenshotProvider : public QQuickImageProvider
244+{
245+public:
246+ WindowScreenshotProvider();
247+
248+ // id is ignored for now
249+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
250+};
251+
252+#endif // WINDOW_SCREENSHOT_PROVIDER_H_
253
254=== modified file 'qml/CMakeLists.txt'
255--- qml/CMakeLists.txt 2014-12-16 16:49:49 +0000
256+++ qml/CMakeLists.txt 2015-04-16 12:11:39 +0000
257@@ -13,6 +13,7 @@
258 Notifications
259 Panel
260 Stages
261+ Rotation
262 Tutorial
263 Wizard
264 )
265
266=== added file 'qml/Components/WindowScreenshot.qml'
267--- qml/Components/WindowScreenshot.qml 1970-01-01 00:00:00 +0000
268+++ qml/Components/WindowScreenshot.qml 2015-04-16 12:11:39 +0000
269@@ -0,0 +1,35 @@
270+/*
271+ * Copyright (C) 2015 Canonical, Ltd.
272+ *
273+ * This program is free software; you can redistribute it and/or modify
274+ * it under the terms of the GNU General Public License as published by
275+ * the Free Software Foundation; version 3.
276+ *
277+ * This program is distributed in the hope that it will be useful,
278+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
279+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
280+ * GNU General Public License for more details.
281+ *
282+ * You should have received a copy of the GNU General Public License
283+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
284+ */
285+
286+import QtQuick 2.2
287+
288+Item {
289+ id: root
290+
291+ function take() {
292+ var timeNow = new Date().getTime();
293+ image.source = "image://window/" + timeNow;
294+ }
295+
296+ // Unload the image to free up memory
297+ function discard() {
298+ image.source = "";
299+ }
300+
301+ Image {
302+ id: image
303+ }
304+}
305
306=== added file 'qml/Dash/graphics/phone/screenshots/gmail-webapp.svg'
307--- qml/Dash/graphics/phone/screenshots/gmail-webapp.svg 1970-01-01 00:00:00 +0000
308+++ qml/Dash/graphics/phone/screenshots/gmail-webapp.svg 2015-04-16 12:11:39 +0000
309@@ -0,0 +1,343 @@
310+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
311+<!-- Created with Inkscape (http://www.inkscape.org/) -->
312+
313+<svg
314+ xmlns:dc="http://purl.org/dc/elements/1.1/"
315+ xmlns:cc="http://creativecommons.org/ns#"
316+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
317+ xmlns:svg="http://www.w3.org/2000/svg"
318+ xmlns="http://www.w3.org/2000/svg"
319+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
320+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
321+ width="768"
322+ height="1280"
323+ id="svg2"
324+ version="1.1"
325+ inkscape:version="0.48.5 r10040"
326+ sodipodi:docname="gmail-webapp.svg">
327+ <defs
328+ id="defs4" />
329+ <sodipodi:namedview
330+ id="base"
331+ pagecolor="#ffffff"
332+ bordercolor="#666666"
333+ borderopacity="1.0"
334+ inkscape:pageopacity="0.0"
335+ inkscape:pageshadow="2"
336+ inkscape:zoom="0.49497475"
337+ inkscape:cx="117.33439"
338+ inkscape:cy="668.80479"
339+ inkscape:document-units="px"
340+ inkscape:current-layer="layer1"
341+ showgrid="false"
342+ inkscape:window-width="1920"
343+ inkscape:window-height="1056"
344+ inkscape:window-x="0"
345+ inkscape:window-y="24"
346+ inkscape:window-maximized="1" />
347+ <metadata
348+ id="metadata7">
349+ <rdf:RDF>
350+ <cc:Work
351+ rdf:about="">
352+ <dc:format>image/svg+xml</dc:format>
353+ <dc:type
354+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
355+ <dc:title></dc:title>
356+ </cc:Work>
357+ </rdf:RDF>
358+ </metadata>
359+ <g
360+ inkscape:label="Layer 1"
361+ inkscape:groupmode="layer"
362+ id="layer1"
363+ transform="translate(0,227.63782)">
364+ <rect
365+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
366+ id="rect2985"
367+ width="769.73627"
368+ height="1276.8328"
369+ x="-2.0203052"
370+ y="3.1671834"
371+ transform="translate(0,-227.63782)" />
372+ <rect
373+ style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
374+ id="rect3797"
375+ width="773.77686"
376+ height="129.29953"
377+ x="-6.0609155"
378+ y="-0.87342685"
379+ transform="translate(0,-227.63782)" />
380+ <text
381+ xml:space="preserve"
382+ style="font-size:72px;font-style:normal;font-weight:500;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium;font-stretch:normal;font-variant:normal"
383+ x="20.203053"
384+ y="-139.61781"
385+ id="text3755"
386+ sodipodi:linespacing="125%"><tspan
387+ sodipodi:role="line"
388+ id="tspan3757"
389+ x="20.203053"
390+ y="-139.61781">GMail</tspan></text>
391+ <text
392+ xml:space="preserve"
393+ style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
394+ x="14.142137"
395+ y="-28.501045"
396+ id="text3759"
397+ sodipodi:linespacing="125%"><tspan
398+ sodipodi:role="line"
399+ id="tspan3761"
400+ x="14.142137"
401+ y="-28.501045">Inbox</tspan></text>
402+ <path
403+ sodipodi:type="arc"
404+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
405+ id="path3765"
406+ sodipodi:cx="107.07617"
407+ sodipodi:cy="337.52768"
408+ sodipodi:rx="64.649765"
409+ sodipodi:ry="61.619305"
410+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
411+ transform="translate(-28.284271,-251.88148)" />
412+ <path
413+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
414+ d="m 14.142136,237.52257 729.330134,0"
415+ id="path3769"
416+ inkscape:connector-curvature="0"
417+ transform="translate(0,-227.63782)" />
418+ <text
419+ xml:space="preserve"
420+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
421+ x="167.68533"
422+ y="78.575127"
423+ id="text3771"
424+ sodipodi:linespacing="125%"><tspan
425+ sodipodi:role="line"
426+ id="tspan3773"
427+ x="167.68533"
428+ y="78.575127">Lorem ipsum</tspan></text>
429+ <text
430+ xml:space="preserve"
431+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
432+ x="167.68532"
433+ y="131.10306"
434+ id="text3775"
435+ sodipodi:linespacing="125%"><tspan
436+ sodipodi:role="line"
437+ id="tspan3777"
438+ x="167.68532"
439+ y="131.10306"
440+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
441+ <path
442+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
443+ d="m 13.13199,165.44825 729.33013,0"
444+ id="path3769-2"
445+ inkscape:connector-curvature="0" />
446+ <path
447+ sodipodi:type="arc"
448+ style="fill:#8eff58;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
449+ id="path3765-3"
450+ sodipodi:cx="107.07617"
451+ sodipodi:cy="337.52768"
452+ sodipodi:rx="64.649765"
453+ sodipodi:ry="61.619305"
454+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
455+ transform="translate(-26.263969,-93.102383)" />
456+ <text
457+ xml:space="preserve"
458+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
459+ x="169.70564"
460+ y="237.35422"
461+ id="text3771-9"
462+ sodipodi:linespacing="125%"><tspan
463+ sodipodi:role="line"
464+ id="tspan3773-7"
465+ x="169.70564"
466+ y="237.35422">Lorem ipsum</tspan></text>
467+ <text
468+ xml:space="preserve"
469+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
470+ x="169.70561"
471+ y="289.88217"
472+ id="text3775-1"
473+ sodipodi:linespacing="125%"><tspan
474+ sodipodi:role="line"
475+ id="tspan3777-1"
476+ x="169.70561"
477+ y="289.88217"
478+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
479+ <path
480+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
481+ d="m 15.152292,324.22735 729.330128,0"
482+ id="path3769-2-6"
483+ inkscape:connector-curvature="0" />
484+ <path
485+ sodipodi:type="arc"
486+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
487+ id="path3765-7"
488+ sodipodi:cx="107.07617"
489+ sodipodi:cy="337.52768"
490+ sodipodi:rx="64.649765"
491+ sodipodi:ry="61.619305"
492+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
493+ transform="translate(-28.284274,64.481414)" />
494+ <text
495+ xml:space="preserve"
496+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
497+ x="167.68533"
498+ y="394.93799"
499+ id="text3771-92"
500+ sodipodi:linespacing="125%"><tspan
501+ sodipodi:role="line"
502+ id="tspan3773-0"
503+ x="167.68533"
504+ y="394.93799">Lorem ipsum</tspan></text>
505+ <text
506+ xml:space="preserve"
507+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
508+ x="167.68532"
509+ y="447.46594"
510+ id="text3775-9"
511+ sodipodi:linespacing="125%"><tspan
512+ sodipodi:role="line"
513+ id="tspan3777-8"
514+ x="167.68532"
515+ y="447.46594"
516+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
517+ <path
518+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
519+ d="m 13.131986,481.81114 729.330124,0"
520+ id="path3769-2-3"
521+ inkscape:connector-curvature="0" />
522+ <path
523+ sodipodi:type="arc"
524+ style="fill:#8ea2ff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
525+ id="path3765-2"
526+ sodipodi:cx="107.07617"
527+ sodipodi:cy="337.52768"
528+ sodipodi:rx="64.649765"
529+ sodipodi:ry="61.619305"
530+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
531+ transform="translate(-26.263969,226.10582)" />
532+ <text
533+ xml:space="preserve"
534+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
535+ x="169.70564"
536+ y="556.56238"
537+ id="text3771-1"
538+ sodipodi:linespacing="125%"><tspan
539+ sodipodi:role="line"
540+ id="tspan3773-1"
541+ x="169.70564"
542+ y="556.56238">Lorem ipsum</tspan></text>
543+ <text
544+ xml:space="preserve"
545+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
546+ x="169.70563"
547+ y="609.09033"
548+ id="text3775-8"
549+ sodipodi:linespacing="125%"><tspan
550+ sodipodi:role="line"
551+ id="tspan3777-85"
552+ x="169.70563"
553+ y="609.09033"
554+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
555+ <path
556+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
557+ d="m 15.152292,643.43555 729.330128,0"
558+ id="path3769-2-1"
559+ inkscape:connector-curvature="0" />
560+ <path
561+ sodipodi:type="arc"
562+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
563+ id="path3765-8"
564+ sodipodi:cx="107.07617"
565+ sodipodi:cy="337.52768"
566+ sodipodi:rx="64.649765"
567+ sodipodi:ry="61.619305"
568+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
569+ transform="translate(-24.243664,391.77084)" />
570+ <text
571+ xml:space="preserve"
572+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
573+ x="171.72594"
574+ y="722.22742"
575+ id="text3771-7"
576+ sodipodi:linespacing="125%"><tspan
577+ sodipodi:role="line"
578+ id="tspan3773-6"
579+ x="171.72594"
580+ y="722.22742">Lorem ipsum</tspan></text>
581+ <text
582+ xml:space="preserve"
583+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
584+ x="171.72592"
585+ y="774.75537"
586+ id="text3775-5"
587+ sodipodi:linespacing="125%"><tspan
588+ sodipodi:role="line"
589+ id="tspan3777-2"
590+ x="171.72592"
591+ y="774.75537"
592+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
593+ <path
594+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
595+ d="m 17.172597,809.10057 729.330133,0"
596+ id="path3769-2-13"
597+ inkscape:connector-curvature="0" />
598+ <path
599+ sodipodi:type="arc"
600+ style="fill:#e44738;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
601+ id="path3765-9"
602+ sodipodi:cx="107.07617"
603+ sodipodi:cy="337.52768"
604+ sodipodi:rx="64.649765"
605+ sodipodi:ry="61.619305"
606+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
607+ transform="translate(-24.243664,557.43585)" />
608+ <text
609+ xml:space="preserve"
610+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
611+ x="171.72594"
612+ y="887.89246"
613+ id="text3771-2"
614+ sodipodi:linespacing="125%"><tspan
615+ sodipodi:role="line"
616+ id="tspan3773-8"
617+ x="171.72594"
618+ y="887.89246">Lorem ipsum</tspan></text>
619+ <text
620+ xml:space="preserve"
621+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
622+ x="171.72592"
623+ y="940.42041"
624+ id="text3775-3"
625+ sodipodi:linespacing="125%"><tspan
626+ sodipodi:role="line"
627+ id="tspan3777-7"
628+ x="171.72592"
629+ y="940.42041"
630+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
631+ <path
632+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
633+ d="m 17.172597,974.76558 729.330123,0"
634+ id="path3769-2-9"
635+ inkscape:connector-curvature="0" />
636+ <path
637+ sodipodi:type="arc"
638+ style="fill:none;stroke:#000000;stroke-width:8;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
639+ id="path3929"
640+ sodipodi:cx="590.93921"
641+ sodipodi:cy="-119.06127"
642+ sodipodi:rx="33.335033"
643+ sodipodi:ry="33.335033"
644+ d="m 624.27424,-119.06127 a 33.335033,33.335033 0 1 1 -66.67006,0 33.335033,33.335033 0 1 1 66.67006,0 z"
645+ transform="matrix(0.85096826,0,0,0.85096826,175.43863,-68.81729)" />
646+ <path
647+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:3.40387297;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
648+ d="m 692.92249,-145.20602 20.63059,24.06901 12.03451,-13.75372 -20.63059,-17.19215"
649+ id="path3931"
650+ inkscape:connector-curvature="0" />
651+ </g>
652+</svg>
653
654=== removed file 'qml/Dash/graphics/phone/screenshots/settings@12.png'
655Binary files qml/Dash/graphics/phone/screenshots/settings@12.png 2013-06-20 13:42:39 +0000 and qml/Dash/graphics/phone/screenshots/settings@12.png 1970-01-01 00:00:00 +0000 differ
656=== added file 'qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svg'
657--- qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svg 1970-01-01 00:00:00 +0000
658+++ qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svg 2015-04-16 12:11:39 +0000
659@@ -0,0 +1,201 @@
660+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
661+<!-- Created with Inkscape (http://www.inkscape.org/) -->
662+
663+<svg
664+ xmlns:dc="http://purl.org/dc/elements/1.1/"
665+ xmlns:cc="http://creativecommons.org/ns#"
666+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
667+ xmlns:svg="http://www.w3.org/2000/svg"
668+ xmlns="http://www.w3.org/2000/svg"
669+ xmlns:xlink="http://www.w3.org/1999/xlink"
670+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
671+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
672+ width="829"
673+ height="480"
674+ id="svg2740"
675+ sodipodi:version="0.32"
676+ inkscape:version="0.48.5 r10040"
677+ version="1.0"
678+ sodipodi:docname="ubuntu-weather-app.svg"
679+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
680+ <defs
681+ id="defs2742">
682+ <linearGradient
683+ id="linearGradient3824">
684+ <stop
685+ style="stop-color:#e6e6e6;stop-opacity:1;"
686+ offset="0"
687+ id="stop3826" />
688+ <stop
689+ style="stop-color:#23abff;stop-opacity:1;"
690+ offset="1"
691+ id="stop3828" />
692+ </linearGradient>
693+ <linearGradient
694+ inkscape:collect="always"
695+ xlink:href="#linearGradient3824"
696+ id="linearGradient3830"
697+ x1="348.55862"
698+ y1="343.23914"
699+ x2="348.55862"
700+ y2="-17.422215"
701+ gradientUnits="userSpaceOnUse" />
702+ </defs>
703+ <sodipodi:namedview
704+ id="base"
705+ pagecolor="#ffffff"
706+ bordercolor="#666666"
707+ borderopacity="1.0"
708+ gridtolerance="10000"
709+ guidetolerance="10"
710+ objecttolerance="10"
711+ inkscape:pageopacity="0.0"
712+ inkscape:pageshadow="2"
713+ inkscape:zoom="0.82625984"
714+ inkscape:cx="331.28234"
715+ inkscape:cy="125.54212"
716+ inkscape:document-units="px"
717+ inkscape:current-layer="layer1"
718+ showgrid="false"
719+ inkscape:window-width="1145"
720+ inkscape:window-height="847"
721+ inkscape:window-x="268"
722+ inkscape:window-y="63"
723+ inkscape:window-maximized="0" />
724+ <metadata
725+ id="metadata2745">
726+ <rdf:RDF>
727+ <cc:Work
728+ rdf:about="">
729+ <dc:format>image/svg+xml</dc:format>
730+ <dc:type
731+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
732+ </cc:Work>
733+ </rdf:RDF>
734+ </metadata>
735+ <g
736+ inkscape:label="Layer 1"
737+ inkscape:groupmode="layer"
738+ id="layer1"
739+ transform="translate(-321.13452,-104.68346)">
740+ <rect
741+ style="fill:url(#linearGradient3830);fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
742+ id="rect2990"
743+ width="832.66785"
744+ height="480.47839"
745+ x="-1.210273"
746+ y="0.73188055"
747+ transform="translate(321.13452,104.68346)" />
748+ <path
749+ d="m 310.66382,132.06057 c -1.82703,2.0443 -22.21039,-18.38308 -24.89334,-17.37811 -2.68295,1.00497 -6.73783,33.64192 -9.57527,34.01165 -2.83744,0.36973 -7.71546,-33.08881 -10.48084,-33.88783 -2.76539,-0.79903 -18.7793,19.34152 -21.17964,17.82892 -2.40034,-1.51259 5.69972,-26.09808 4.36486,-28.67057 -1.33487,-2.57249 -31.86305,-3.78032 -32.49182,-6.6501 -0.62876,-2.86978 27.66855,-10.399376 27.90248,-13.241764 0.23394,-2.842387 -18.99828,-18.90215 -17.89698,-21.393798 1.10131,-2.491647 26.85456,8.177399 28.59429,5.99159 1.73972,-2.185809 -3.39182,-29.683489 -0.75169,-30.507098 2.64013,-0.823608 18.50306,18.485327 21.32153,18.392972 2.81847,-0.09235 11.30312,-24.287685 14.18258,-23.722317 2.87947,0.565368 1.44329,25.667631 3.67226,27.34365 2.22897,1.676019 24.27161,-6.28288 25.90675,-4.004945 1.63514,2.277936 -12.63923,24.445025 -11.77955,27.07291 0.85968,2.627885 27.01183,5.1355 26.65457,7.852474 -0.35726,2.716973 -32.69913,3.875594 -34.04684,6.507203 -1.3477,2.631613 12.32368,32.410863 10.49665,34.455163 z"
750+ id="path11949"
751+ inkscape:flatsided="false"
752+ inkscape:randomized="-0.092"
753+ inkscape:rounded="0.1"
754+ sodipodi:arg1="0.79570711"
755+ sodipodi:arg2="1.144773"
756+ sodipodi:cx="275.15002"
757+ sodipodi:cy="88.090233"
758+ sodipodi:r1="57.019234"
759+ sodipodi:r2="32.818508"
760+ sodipodi:sides="9"
761+ sodipodi:type="star"
762+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible"
763+ transform="matrix(2.527571,0,0,2.527571,85.214779,60.619097)" />
764+ <path
765+ d="m 194.51692,77.283737 c 0,20.659371 -17.86424,37.407103 -39.90091,37.407103 -22.03666,0 -39.90091,-16.747732 -39.90091,-37.407103 0,-20.659371 17.86425,-37.4071 39.90091,-37.4071 22.03667,0 39.90091,16.747729 39.90091,37.4071 z"
766+ id="path11951"
767+ sodipodi:cx="154.61601"
768+ sodipodi:cy="77.283737"
769+ sodipodi:rx="39.900909"
770+ sodipodi:ry="37.407101"
771+ sodipodi:type="arc"
772+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:#f5ff12;stroke-width:10.27388287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
773+ transform="matrix(2.4568175,0,0,2.1990879,404.91256,120.05076)" />
774+ <path
775+ transform="matrix(3.2663208,0,0,3.4016021,515.92101,-254.59331)"
776+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:7.30059433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
777+ sodipodi:type="inkscape:offset"
778+ inkscape:radius="0"
779+ inkscape:original="M 110.53125 144.96875 C 100.76599 144.96875 92.384060 150.03292 88.281250 157.34375 C 86.642607 156.42892 84.835190 155.87500 82.906250 155.87500 C 77.073071 155.87500 72.219030 160.56753 70.718750 166.93750 C 68.157973 165.92533 65.180470 165.31250 62.000000 165.31250 C 52.458571 165.31250 44.718750 170.53296 44.718750 176.96875 C 44.718749 182.77816 51.066430 187.55402 59.312500 188.43750 C 58.942922 189.06456 58.656250 189.68551 58.656250 190.34375 C 58.656248 196.50568 75.538140 201.53125 96.343750 201.53125 C 117.14936 201.53125 134.03126 196.50568 134.03125 190.34375 C 134.03125 189.92916 133.77393 189.55916 133.62500 189.15625 C 134.53629 189.38287 135.43860 189.62500 136.43750 189.62500 C 142.00333 189.62501 146.53125 185.84318 146.53125 181.18750 C 146.53125 176.53182 142.00332 172.75000 136.43750 172.75000 C 135.75542 172.75000 135.17544 172.98560 134.53125 173.09375 C 135.09454 171.23218 135.46875 169.33268 135.46875 167.31250 C 135.46875 154.98864 124.31330 144.96875 110.53125 144.96875 z "
780+ id="path11953"
781+ d="m 110.53125,144.96875 c -9.76526,0 -18.14719,5.06417 -22.25,12.375 -1.638643,-0.91483 -3.44606,-1.46875 -5.375,-1.46875 -5.833179,0 -10.68722,4.69253 -12.1875,11.0625 -2.560777,-1.01217 -5.53828,-1.625 -8.71875,-1.625 -9.541429,0 -17.28125,5.22046 -17.28125,11.65625 -10e-7,5.80941 6.34768,10.58527 14.59375,11.46875 -0.369578,0.62706 -0.65625,1.24801 -0.65625,1.90625 -2e-6,6.16193 16.88189,11.1875 37.6875,11.1875 20.80561,0 37.68751,-5.02557 37.6875,-11.1875 0,-0.41459 -0.25732,-0.78459 -0.40625,-1.1875 0.91129,0.22662 1.8136,0.46875 2.8125,0.46875 5.56583,1e-5 10.09375,-3.78182 10.09375,-8.4375 0,-4.65568 -4.52793,-8.4375 -10.09375,-8.4375 -0.68208,0 -1.26206,0.2356 -1.90625,0.34375 0.56329,-1.86157 0.9375,-3.76107 0.9375,-5.78125 0,-12.32386 -11.15545,-22.34375 -24.9375,-22.34375 z" />
782+ <path
783+ inkscape:connector-curvature="0"
784+ style="fill:#386195;fill-opacity:1;fill-rule:nonzero;stroke:#386195;stroke-width:24.07697487;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
785+ id="path11963"
786+ d="m 975.70833,305.75079 c -17.87875,0 -33.1897,9.10731 -40.70137,22.18913 -3.00012,-1.63696 -6.34124,-2.6392 -9.87286,-2.6392 -10.67967,0 -19.5639,8.39604 -22.31073,19.7943 -4.6884,-1.81117 -10.09942,-2.93251 -15.92242,-2.93251 -17.46892,0 -31.65122,9.35352 -31.65122,20.86955 0,10.39528 11.61748,18.9466 26.71485,20.52749 -0.67665,1.12204 -1.20995,2.24338 -1.20995,3.42121 0,11.02604 30.92124,19.98985 69.01327,19.98985 38.09195,0 69.0132,-8.96381 69.0132,-19.98985 0,-0.74183 -0.5017,-1.42952 -0.7743,-2.15046 1.6684,0.40556 3.3496,0.83088 5.1784,0.83088 10.1902,0 18.439,-6.7716 18.439,-15.10234 0,-8.33078 -8.2488,-15.05348 -18.439,-15.05348 -1.2488,0 -2.3535,0.39297 -3.5329,0.58647 1.0312,-3.33102 1.7422,-6.7466 1.7422,-10.36143 0,-22.05212 -20.4533,-39.97961 -45.68617,-39.97961 z" />
787+ <path
788+ inkscape:connector-curvature="0"
789+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
790+ sodipodi:nodetypes="cccc"
791+ id="path11965"
792+ d="m 717.80594,482.73655 c -32.27924,28.4432 5.62151,52.69793 20.03284,13.63938 l 11.08203,-36.22961 -31.11487,22.59023 z" />
793+ <path
794+ inkscape:connector-curvature="0"
795+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
796+ sodipodi:nodetypes="cccc"
797+ id="path11967"
798+ d="m 799.47,485.76116 c -32.27925,28.4432 5.62151,52.69792 20.03284,13.63937 l 11.08202,-36.2296 -31.11486,22.59023 z" />
799+ <text
800+ xml:space="preserve"
801+ style="font-size:32px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
802+ x="68.985565"
803+ y="92.712631"
804+ id="text2992"
805+ sodipodi:linespacing="125%"
806+ transform="translate(321.13452,104.68346)"><tspan
807+ sodipodi:role="line"
808+ id="tspan2994"
809+ x="68.985565"
810+ y="92.712631" /></text>
811+ <text
812+ xml:space="preserve"
813+ style="font-size:10px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
814+ x="-202.1156"
815+ y="155.64684"
816+ id="text2996"
817+ sodipodi:linespacing="125%"
818+ transform="translate(321.13452,104.68346)"><tspan
819+ sodipodi:role="line"
820+ id="tspan2998"
821+ x="-202.1156"
822+ y="155.64684" /></text>
823+ <text
824+ xml:space="preserve"
825+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
826+ x="342.91943"
827+ y="155.03653"
828+ id="text3000"
829+ sodipodi:linespacing="125%"><tspan
830+ sodipodi:role="line"
831+ id="tspan3002"
832+ x="342.91943"
833+ y="155.03653"
834+ style="font-size:32px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium">Wheather App</tspan></text>
835+ <text
836+ xml:space="preserve"
837+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
838+ x="460.31592"
839+ y="358.3624"
840+ id="text3004"
841+ sodipodi:linespacing="125%"><tspan
842+ sodipodi:role="line"
843+ id="tspan3006"
844+ x="460.31592"
845+ y="358.3624"
846+ style="font-size:48px;fill:#ff0000;-inkscape-font-specification:Ubuntu;font-family:Ubuntu;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">22C</tspan></text>
847+ <text
848+ xml:space="preserve"
849+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
850+ x="460.19751"
851+ y="412.14325"
852+ id="text3004-0"
853+ sodipodi:linespacing="125%"><tspan
854+ sodipodi:role="line"
855+ id="tspan3006-4"
856+ x="460.19751"
857+ y="412.14325"
858+ style="font-size:48px;fill:#0000ff;-inkscape-font-specification:Ubuntu Medium;font-family:Ubuntu;font-weight:500;font-style:normal;font-stretch:normal;font-variant:normal">14C</tspan></text>
859+ </g>
860+</svg>
861
862=== added file 'qml/DeviceConfiguration.qml'
863--- qml/DeviceConfiguration.qml 1970-01-01 00:00:00 +0000
864+++ qml/DeviceConfiguration.qml 2015-04-16 12:11:39 +0000
865@@ -0,0 +1,81 @@
866+/*
867+ * Copyright (C) 2015 Canonical, Ltd.
868+ *
869+ * This program is free software; you can redistribute it and/or modify
870+ * it under the terms of the GNU General Public License as published by
871+ * the Free Software Foundation; version 3.
872+ *
873+ * This program is distributed in the hope that it will be useful,
874+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
875+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
876+ * GNU General Public License for more details.
877+ *
878+ * You should have received a copy of the GNU General Public License
879+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
880+ */
881+
882+import QtQuick 2.0
883+
884+StateGroup {
885+ id: root
886+
887+ readonly property int useNativeOrientation: -1
888+
889+ property int primaryOrientation: useNativeOrientation
890+
891+ property int supportedOrientations: Qt.PortraitOrientation
892+ | Qt.InvertedPortraitOrientation
893+ | Qt.LandscapeOrientation
894+ | Qt.InvertedLandscapeOrientation
895+
896+ // Supported values so far:
897+ // "phone", "tablet" or "desktop"
898+ property string category: "phone"
899+
900+
901+ property alias name: root.state
902+
903+ states: [
904+ State {
905+ name: "mako"
906+ PropertyChanges {
907+ target: root
908+ supportedOrientations: Qt.PortraitOrientation
909+ | Qt.LandscapeOrientation
910+ | Qt.InvertedLandscapeOrientation
911+ }
912+ },
913+ State {
914+ name: "krillin"
915+ PropertyChanges {
916+ target: root
917+ supportedOrientations: Qt.PortraitOrientation
918+ | Qt.LandscapeOrientation
919+ | Qt.InvertedLandscapeOrientation
920+ }
921+ },
922+ State {
923+ name: "manta"
924+ PropertyChanges {
925+ target: root
926+ category: "tablet"
927+ }
928+ },
929+ State {
930+ name: "flo"
931+ PropertyChanges {
932+ target: root
933+ primaryOrientation: Qt.InvertedLandscapeOrientation
934+ category: "tablet"
935+ }
936+ },
937+ State {
938+ name: "desktop"
939+ PropertyChanges {
940+ target: root
941+ category: "desktop"
942+ }
943+ }
944+ ]
945+
946+}
947
948=== modified file 'qml/Greeter/Greeter.qml'
949--- qml/Greeter/Greeter.qml 2015-03-18 10:17:28 +0000
950+++ qml/Greeter/Greeter.qml 2015-04-16 12:11:39 +0000
951@@ -52,6 +52,8 @@
952 property int failedLoginsDelayAttempts: 7 // number of failed logins
953 property real failedLoginsDelayMinutes: 5 // minutes of forced waiting
954
955+ readonly property bool animating: loader.item ? loader.item.animating : false
956+
957 signal tease()
958 signal sessionStarted()
959 signal emergencyCall()
960
961=== modified file 'qml/Greeter/NarrowView.qml'
962--- qml/Greeter/NarrowView.qml 2015-02-23 15:43:41 +0000
963+++ qml/Greeter/NarrowView.qml 2015-04-16 12:11:39 +0000
964@@ -33,6 +33,7 @@
965 property alias infographicModel: coverPage.infographicModel
966 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
967 readonly property bool required: coverPage.required || lockscreen.required
968+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
969
970 signal selected(int index) // unused
971 signal responded(string response)
972
973=== modified file 'qml/Greeter/WideView.qml'
974--- qml/Greeter/WideView.qml 2015-02-23 15:43:41 +0000
975+++ qml/Greeter/WideView.qml 2015-04-16 12:11:39 +0000
976@@ -32,6 +32,7 @@
977 property alias infographicModel: coverPage.infographicModel
978 readonly property bool fullyShown: coverPage.showProgress === 1
979 readonly property bool required: coverPage.required
980+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
981
982 // so that it can be replaced in tests with a mock object
983 property var inputMethod: Qt.inputMethod
984
985=== added file 'qml/OrientedShell.qml'
986--- qml/OrientedShell.qml 1970-01-01 00:00:00 +0000
987+++ qml/OrientedShell.qml 2015-04-16 12:11:39 +0000
988@@ -0,0 +1,182 @@
989+/*
990+ * Copyright (C) 2015 Canonical, Ltd.
991+ *
992+ * This program is free software; you can redistribute it and/or modify
993+ * it under the terms of the GNU General Public License as published by
994+ * the Free Software Foundation; version 3.
995+ *
996+ * This program is distributed in the hope that it will be useful,
997+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
998+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
999+ * GNU General Public License for more details.
1000+ *
1001+ * You should have received a copy of the GNU General Public License
1002+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1003+ */
1004+
1005+import QtQuick 2.0
1006+import QtQuick.Window 2.0
1007+import Unity.Session 0.1
1008+import GSettings 1.0
1009+import "Components"
1010+import "Rotation"
1011+
1012+Rectangle {
1013+ id: root
1014+ color: "black"
1015+
1016+ // NB: native and primary orientations here don't map exactly to their QScreen counterparts
1017+ readonly property int nativeOrientation: width > height ? Qt.LandscapeOrientation : Qt.PortraitOrientation
1018+
1019+ readonly property int primaryOrientation:
1020+ deviceConfiguration.primaryOrientation == deviceConfiguration.useNativeOrientation
1021+ ? nativeOrientation : deviceConfiguration.primaryOrientation
1022+
1023+ DeviceConfiguration {
1024+ id: deviceConfiguration
1025+ name: applicationArguments.deviceName
1026+ }
1027+
1028+ // to be overwritten by tests
1029+ property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
1030+ property int physicalOrientation: Screen.orientation
1031+ property bool orientationLocked: OrientationLock.enabled
1032+ property var orientationLock: OrientationLock
1033+
1034+ property int orientation
1035+ onPhysicalOrientationChanged: {
1036+ if (!orientationLocked) {
1037+ orientation = physicalOrientation;
1038+ }
1039+ }
1040+ onOrientationLockedChanged: {
1041+ if (orientationLocked) {
1042+ orientationLock.savedOrientation = physicalOrientation;
1043+ } else {
1044+ orientation = physicalOrientation;
1045+ }
1046+ }
1047+ Component.onCompleted: {
1048+ if (orientationLocked) {
1049+ orientation = orientationLock.savedOrientation;
1050+ }
1051+ }
1052+
1053+ readonly property int supportedOrientations: shell.supportedOrientations
1054+ & deviceConfiguration.supportedOrientations
1055+ property int acceptedOrientationAngle: {
1056+ if (orientation & supportedOrientations) {
1057+ return Screen.angleBetween(nativeOrientation, orientation);
1058+ } else if (shell.orientation & supportedOrientations) {
1059+ // stay where we are
1060+ return shell.orientationAngle;
1061+ } else if (angleToOrientation(shell.mainAppWindowOrientationAngle) & supportedOrientations) {
1062+ return shell.mainAppWindowOrientationAngle;
1063+ } else {
1064+ // rotate to some supported orientation as we can't stay where we currently are
1065+ // TODO: Choose the closest to the current one
1066+ if (supportedOrientations & Qt.PortraitOrientation) {
1067+ return Screen.angleBetween(nativeOrientation, Qt.PortraitOrientation);
1068+ } else if (supportedOrientations & Qt.LandcscapeOrientation) {
1069+ return Screen.angleBetween(nativeOrientation, Qt.LandscapeOrientation);
1070+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
1071+ return Screen.angleBetween(nativeOrientation, Qt.InvertedPortraitOrientation);
1072+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
1073+ return Screen.angleBetween(nativeOrientation, Qt.InvertedLandscapeOrientation);
1074+ } else {
1075+ // if all fails, fallback to primary orientation
1076+ return Screen.angleBetween(nativeOrientation, primaryOrientation);
1077+ }
1078+ }
1079+ }
1080+
1081+ function angleToOrientation(angle) {
1082+ switch (angle) {
1083+ case 0:
1084+ return nativeOrientation;
1085+ break;
1086+ case 90:
1087+ return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
1088+ : Qt.PortraitOrientation;
1089+ break;
1090+ case 180:
1091+ return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
1092+ : Qt.InvertedLandscapeOrientation;
1093+ break;
1094+ case 270:
1095+ return nativeOrientation === Qt.PortraitOrientation ? Qt.LandscapeOrientation
1096+ : Qt.InvertedPortraitOrientation;
1097+ break;
1098+ default:
1099+ console.warn("angleToOrientation: Invalid orientation angle: " + angle);
1100+ return primaryOrientation;
1101+ }
1102+ }
1103+
1104+ RotationStates {
1105+ id: rotationStates
1106+ objectName: "rotationStates"
1107+ orientedShell: root
1108+ shell: shell
1109+ shellCover: shellCover
1110+ windowScreenshot: windowScreenshot
1111+ }
1112+
1113+ Shell {
1114+ id: shell
1115+ objectName: "shell"
1116+ width: root.width
1117+ height: root.height
1118+ orientation: root.angleToOrientation(orientationAngle)
1119+ primaryOrientation: root.primaryOrientation
1120+ nativeOrientation: root.nativeOrientation
1121+ nativeWidth: root.width
1122+ nativeHeight: root.height
1123+
1124+ // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
1125+ // what's the output device (eg: big TV, desktop monitor, phone display), etc.
1126+ usageScenario: {
1127+ if (root.usageModeSettings.usageMode === "Windowed") {
1128+ return "desktop";
1129+ } else if (root.usageModeSettings.usageMode === "Staged"
1130+ && deviceConfiguration.category === "desktop") {
1131+ return "tablet";
1132+ } else {
1133+ return deviceConfiguration.category;
1134+ }
1135+ }
1136+
1137+ property real transformRotationAngle
1138+ property real transformOriginX
1139+ property real transformOriginY
1140+
1141+ transform: Rotation {
1142+ origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 }
1143+ angle: shell.transformRotationAngle
1144+ }
1145+ }
1146+
1147+ Rectangle {
1148+ id: shellCover
1149+ color: "black"
1150+ anchors.fill: parent
1151+ visible: false
1152+ }
1153+
1154+ WindowScreenshot {
1155+ id: windowScreenshot
1156+ visible: false
1157+ width: root.width
1158+ height: root.height
1159+
1160+ property real transformRotationAngle
1161+ property real transformOriginX
1162+ property real transformOriginY
1163+
1164+ transform: Rotation {
1165+ origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
1166+ axis { x: 0; y: 0; z: 1 }
1167+ angle: windowScreenshot.transformRotationAngle
1168+ }
1169+ }
1170+}
1171
1172=== modified file 'qml/Panel/Panel.qml'
1173--- qml/Panel/Panel.qml 2015-04-02 15:08:05 +0000
1174+++ qml/Panel/Panel.qml 2015-04-16 12:11:39 +0000
1175@@ -27,6 +27,9 @@
1176 property alias indicators: __indicators
1177 property alias callHint: __callHint
1178 property bool fullscreenMode: false
1179+ property real indicatorAreaShowProgress: 1.0
1180+
1181+ opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
1182
1183 Rectangle {
1184 id: darkenedArea
1185@@ -58,6 +61,12 @@
1186 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
1187 }
1188
1189+ transform: Translate {
1190+ y: indicators.state === "initial"
1191+ ? (1.0 - indicatorAreaShowProgress) * -d.indicatorHeight
1192+ : 0
1193+ }
1194+
1195 BorderImage {
1196 id: dropShadow
1197 anchors {
1198
1199=== added directory 'qml/Rotation'
1200=== added file 'qml/Rotation/HalfLoopRotationAnimation.qml'
1201--- qml/Rotation/HalfLoopRotationAnimation.qml 1970-01-01 00:00:00 +0000
1202+++ qml/Rotation/HalfLoopRotationAnimation.qml 2015-04-16 12:11:39 +0000
1203@@ -0,0 +1,46 @@
1204+/*
1205+ * Copyright (C) 2015 Canonical, Ltd.
1206+ *
1207+ * This program is free software; you can redistribute it and/or modify
1208+ * it under the terms of the GNU General Public License as published by
1209+ * the Free Software Foundation; version 3.
1210+ *
1211+ * This program is distributed in the hope that it will be useful,
1212+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1213+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1214+ * GNU General Public License for more details.
1215+ *
1216+ * You should have received a copy of the GNU General Public License
1217+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1218+ */
1219+
1220+import QtQuick 2.3
1221+
1222+SequentialAnimation {
1223+ id: root
1224+
1225+ // set from outside
1226+ property int fromAngle
1227+ property int toAngle
1228+ property var info
1229+ property var shell
1230+
1231+ readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270
1232+
1233+ ScriptAction { script: {
1234+ info.transitioning = true;
1235+ shell.orientationAngle = root.toAngle;
1236+ shell.x = (orientedShell.width - shell.width) / 2
1237+ shell.y = (orientedShell.height - shell.height) / 2;
1238+ shell.transformOriginX = shell.width / 2;
1239+ shell.transformOriginY = shell.height / 2;
1240+ shell.updateFocusedAppOrientation();
1241+ } }
1242+ NumberAnimation {
1243+ target: shell
1244+ property: "transformRotationAngle"
1245+ from: root.fromAngle; to: root.toAngle
1246+ duration: rotationDuration; easing.type: rotationEasing
1247+ }
1248+ ScriptAction { script: { info.transitioning = false; } }
1249+}
1250
1251=== added file 'qml/Rotation/ImmediateRotationAction.qml'
1252--- qml/Rotation/ImmediateRotationAction.qml 1970-01-01 00:00:00 +0000
1253+++ qml/Rotation/ImmediateRotationAction.qml 2015-04-16 12:11:39 +0000
1254@@ -0,0 +1,45 @@
1255+/*
1256+ * Copyright (C) 2015 Canonical, Ltd.
1257+ *
1258+ * This program is free software; you can redistribute it and/or modify
1259+ * it under the terms of the GNU General Public License as published by
1260+ * the Free Software Foundation; version 3.
1261+ *
1262+ * This program is distributed in the hope that it will be useful,
1263+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1264+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1265+ * GNU General Public License for more details.
1266+ *
1267+ * You should have received a copy of the GNU General Public License
1268+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1269+ */
1270+
1271+import QtQuick 2.3
1272+
1273+ScriptAction {
1274+ property var info
1275+ property var shell
1276+
1277+ script: {
1278+ info.transitioning = true;
1279+ shell.orientationAngle = info.requestedOrientationAngle;
1280+ shell.transformRotationAngle = info.requestedOrientationAngle;
1281+
1282+ // Making bindings as orientedShell's dimensions might wiggle during startup.
1283+ if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
1284+ shell.width = Qt.binding(function() { return orientedShell.height; });
1285+ shell.height = Qt.binding(function() { return orientedShell.width; });
1286+ } else {
1287+ shell.width = Qt.binding(function() { return orientedShell.width; });
1288+ shell.height = Qt.binding(function() { return orientedShell.height; });
1289+ }
1290+
1291+ shell.x = Qt.binding(function() { return (orientedShell.width - shell.width) / 2; });
1292+ shell.y = Qt.binding(function() { return (orientedShell.height - shell.height) / 2; });
1293+ shell.transformOriginX = Qt.binding(function() { return shell.width / 2; });
1294+ shell.transformOriginY = Qt.binding(function() { return shell.height / 2; });
1295+
1296+ shell.updateFocusedAppOrientation();
1297+ info.transitioning = false;
1298+ }
1299+}
1300
1301=== added file 'qml/Rotation/NinetyRotationAnimation.qml'
1302--- qml/Rotation/NinetyRotationAnimation.qml 1970-01-01 00:00:00 +0000
1303+++ qml/Rotation/NinetyRotationAnimation.qml 2015-04-16 12:11:39 +0000
1304@@ -0,0 +1,90 @@
1305+/*
1306+ * Copyright (C) 2015 Canonical, Ltd.
1307+ *
1308+ * This program is free software; you can redistribute it and/or modify
1309+ * it under the terms of the GNU General Public License as published by
1310+ * the Free Software Foundation; version 3.
1311+ *
1312+ * This program is distributed in the hope that it will be useful,
1313+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1314+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1315+ * GNU General Public License for more details.
1316+ *
1317+ * You should have received a copy of the GNU General Public License
1318+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1319+ */
1320+
1321+import QtQuick 2.3
1322+
1323+SequentialAnimation {
1324+ id: root
1325+
1326+ property int fromAngle
1327+ property int toAngle
1328+ property var info
1329+ property var shell
1330+
1331+ readonly property real fromY: fromAngle === 0 || fromAngle === 90 ? 0 : orientedShell.height - orientedShell.width;
1332+ readonly property real toY: toAngle === 0 || toAngle === 90 ? 0 : orientedShell.height - orientedShell.width;
1333+ readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270
1334+
1335+ ScriptAction { script: {
1336+ info.transitioning = true;
1337+ windowScreenshot.take();
1338+ windowScreenshot.visible = true;
1339+ shell.orientationAngle = root.toAngle;
1340+ shell.x = 0;
1341+ shell.width = flipShellDimensions ? orientedShell.height : orientedShell.width;
1342+ shell.height = flipShellDimensions ? orientedShell.width : orientedShell.height;
1343+ shell.transformOriginX = orientedShell.width / 2;
1344+ shell.transformOriginY = orientedShell.width / 2;
1345+ shell.updateFocusedAppOrientation();
1346+ shellCover.visible = true;
1347+
1348+ windowScreenshot.transformOriginX = orientedShell.width / 2;
1349+ if (fromAngle == 180 || fromAngle == 270) {
1350+ windowScreenshot.transformOriginY = orientedShell.height - (orientedShell.width / 2);
1351+ } else {
1352+ windowScreenshot.transformOriginY = orientedShell.width / 2;
1353+ }
1354+ } }
1355+ ParallelAnimation {
1356+ NumberAnimation {
1357+ target: shellCover; property: "opacity"; from: 1; to: 0;
1358+ duration: rotationDuration; easing.type: rotationEasing
1359+ }
1360+ RotationAnimation {
1361+ target: shell; property: "transformRotationAngle";
1362+ from: root.fromAngle; to: root.toAngle
1363+ direction: RotationAnimation.Shortest
1364+ duration: rotationDuration; easing.type: rotationEasing
1365+ }
1366+ NumberAnimation {
1367+ target: shell; property: "y"
1368+ from: root.fromY; to: root.toY
1369+ duration: rotationDuration; easing.type: rotationEasing
1370+ }
1371+
1372+ NumberAnimation {
1373+ target: windowScreenshot; property: "opacity"; from: 1; to: 0;
1374+ duration: rotationDuration; easing.type: rotationEasing
1375+ }
1376+ RotationAnimation {
1377+ target: windowScreenshot; property: "transformRotationAngle";
1378+ from: 0; to: root.toAngle - root.fromAngle
1379+ direction: RotationAnimation.Shortest
1380+ duration: rotationDuration; easing.type: rotationEasing
1381+ }
1382+ NumberAnimation {
1383+ target: windowScreenshot; property: "y"
1384+ from: 0; to: root.toY - root.fromY
1385+ duration: rotationDuration; easing.type: rotationEasing
1386+ }
1387+ }
1388+ ScriptAction { script: {
1389+ windowScreenshot.visible = false;
1390+ windowScreenshot.discard();
1391+ shellCover.visible = false;
1392+ info.transitioning = false;
1393+ } }
1394+}
1395
1396=== added file 'qml/Rotation/RotationStates.qml'
1397--- qml/Rotation/RotationStates.qml 1970-01-01 00:00:00 +0000
1398+++ qml/Rotation/RotationStates.qml 2015-04-16 12:11:39 +0000
1399@@ -0,0 +1,271 @@
1400+/*
1401+ * Copyright (C) 2015 Canonical, Ltd.
1402+ *
1403+ * This program is free software; you can redistribute it and/or modify
1404+ * it under the terms of the GNU General Public License as published by
1405+ * the Free Software Foundation; version 3.
1406+ *
1407+ * This program is distributed in the hope that it will be useful,
1408+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1409+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1410+ * GNU General Public License for more details.
1411+ *
1412+ * You should have received a copy of the GNU General Public License
1413+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1414+ */
1415+
1416+import QtQuick 2.0
1417+import Ubuntu.Components 1.1
1418+
1419+// Why the state machine is done that way:
1420+// We cannot use regular PropertyChanges{} inside the State elements as steps in the
1421+// transition animations must take place in a well defined order.
1422+// Which means that we also cannot jump to a new state in the middle of a transition
1423+// as that would make hell brake loose.
1424+StateGroup {
1425+ id: root
1426+
1427+ // to be set from the outside
1428+ property Item orientedShell
1429+ property Item shell
1430+ property Item shellCover
1431+ property Item windowScreenshot
1432+
1433+ property int rotationDuration: 450
1434+ property int rotationEasing: Easing.InOutCubic
1435+ // Those values are good for debugging/development
1436+ //property int rotationDuration: 3000
1437+ //property int rotationEasing: Easing.Linear
1438+
1439+ state: "0"
1440+ states: [
1441+ State { name: "0" },
1442+ State { name: "90" },
1443+ State { name: "180" },
1444+ State { name: "270" }
1445+ ]
1446+
1447+ property QtObject d: QtObject {
1448+ id: d
1449+
1450+ property bool startingUp: true
1451+ property var finishStartUpTimer: Timer {
1452+ interval: 500
1453+ onTriggered: d.startingUp = false
1454+ }
1455+ Component.onCompleted: {
1456+ finishStartUpTimer.start();
1457+ }
1458+
1459+ property bool transitioning: false
1460+ onTransitioningChanged: {
1461+ d.tryUpdateState();
1462+ }
1463+
1464+ readonly property int requestedOrientationAngle: root.orientedShell.acceptedOrientationAngle
1465+
1466+ // Avoiding a direct call to tryUpdateState() as the state change might trigger an immediate
1467+ // change to Shell.orientationAngle which, in its turn, causes a reevaluation of
1468+ // requestedOrientationAngle (ie., OrientedShell.acceptedOrientationAngle). A reentrant evaluation
1469+ // of a binding is detected by QML as a binding loop and QML will deny the reevalutation, which
1470+ // will leave us in a bogus state.
1471+ //
1472+ // To avoid this mess we update the state in the next event loop iteration, ensuring a clean
1473+ // call stack.
1474+ onRequestedOrientationAngleChanged: {
1475+ stateUpdateTimer.start();
1476+ }
1477+ property Timer stateUpdateTimer: Timer {
1478+ id: stateUpdateTimer
1479+ interval: 1
1480+ onTriggered: { d.tryUpdateState(); }
1481+ }
1482+
1483+ function tryUpdateState() {
1484+ if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
1485+ return;
1486+ }
1487+
1488+ var requestedState = d.requestedOrientationAngle.toString();
1489+ if (requestedState !== root.state) {
1490+ d.resolveAnimationType();
1491+ root.state = requestedState;
1492+ }
1493+ }
1494+
1495+ property Connections shellConnections: Connections {
1496+ target: root.shell
1497+ onOrientationChangesEnabledChanged: {
1498+ d.tryUpdateState();
1499+ }
1500+ }
1501+
1502+ property var shellBeingResized: Binding {
1503+ target: root.shell
1504+ property: "beingResized"
1505+ value: d.transitioning
1506+ }
1507+
1508+ readonly property int fullAnimation: 0
1509+ readonly property int indicatorsBarAnimation: 1
1510+ readonly property int noAnimation: 2
1511+
1512+ property int animationType
1513+
1514+ // animationType update *must* take place *before* the state update.
1515+ // If animationType and state were updated through bindings, as with normal qml code,
1516+ // there would be no guarantee in the order of the binding updates, which could then
1517+ // cause the wrong transitions to be chosen for the state changes.
1518+ function resolveAnimationType() {
1519+ if (d.startingUp) {
1520+ // During start up, inital property values are still settling while we're still
1521+ // to render the very first frame
1522+ d.animationType = d.noAnimation;
1523+ } else {
1524+ if (!root.shell.mainApp) {
1525+ // shouldn't happen but, anyway
1526+ d.animationType = d.fullAnimation;
1527+ return;
1528+ }
1529+
1530+ if (root.shell.mainApp.rotatesWindowContents) {
1531+ // The application will animate its own GUI, so we don't have to do anything ourselves.
1532+ d.animationType = d.noAnimation;
1533+ } else if (root.shell.mainAppWindowOrientationAngle == d.requestedOrientationAngle) {
1534+ // The app window is already on its final orientation angle.
1535+ // So we just animate the indicators bar
1536+ // TODO: what if the app is fullscreen?
1537+ d.animationType = d.indicatorsBarAnimation;
1538+ } else {
1539+ d.animationType = d.fullAnimation;
1540+ }
1541+ }
1542+ }
1543+
1544+ // When an application switch takes place, d.requestedOrientationAngle and
1545+ // root.shell.mainAppWindowOrientationAngle get updated separately, at different moments.
1546+ // So, when one of those properties change, we shouldn't make a decision straight away
1547+ // as the other might be stale and about to be changed. So let's give it a bit of time for
1548+ // them to get properly updated.
1549+ // This approach is indeed a bit hacky.
1550+ property bool appWindowOrientationAngleNeedsUpdateUnstable:
1551+ root.shell.orientationAngle === d.requestedOrientationAngle
1552+ && root.shell.mainApp
1553+ && root.shell.mainAppWindowOrientationAngle !== root.shell.orientationAngle
1554+ && !d.transitioning
1555+ onAppWindowOrientationAngleNeedsUpdateUnstableChanged: {
1556+ stableTimer.restart();
1557+ }
1558+ property Timer stableTimer: Timer {
1559+ interval: 200
1560+ onTriggered: {
1561+ if (d.appWindowOrientationAngleNeedsUpdateUnstable) {
1562+ shell.updateFocusedAppOrientationAnimated();
1563+ }
1564+ }
1565+ }
1566+ }
1567+
1568+ transitions: [
1569+ Transition {
1570+ from: "90"; to: "0"
1571+ enabled: d.animationType == d.fullAnimation
1572+ NinetyRotationAnimation { fromAngle: 90; toAngle: 0
1573+ info: d; shell: root.shell }
1574+ },
1575+ Transition {
1576+ from: "0"; to: "90"
1577+ enabled: d.animationType == d.fullAnimation
1578+ NinetyRotationAnimation { fromAngle: 0; toAngle: 90
1579+ info: d; shell: root.shell }
1580+ },
1581+ Transition {
1582+ from: "0"; to: "270"
1583+ enabled: d.animationType == d.fullAnimation
1584+ NinetyRotationAnimation { fromAngle: 0; toAngle: 270
1585+ info: d; shell: root.shell }
1586+ },
1587+ Transition {
1588+ from: "270"; to: "0"
1589+ enabled: d.animationType == d.fullAnimation
1590+ NinetyRotationAnimation { fromAngle: 270; toAngle: 0
1591+ info: d; shell: root.shell }
1592+ },
1593+ Transition {
1594+ from: "90"; to: "180"
1595+ enabled: d.animationType == d.fullAnimation
1596+ NinetyRotationAnimation { fromAngle: 90; toAngle: 180
1597+ info: d; shell: root.shell }
1598+ },
1599+ Transition {
1600+ from: "180"; to: "90"
1601+ enabled: d.animationType == d.fullAnimation
1602+ NinetyRotationAnimation { fromAngle: 180; toAngle: 90
1603+ info: d; shell: root.shell }
1604+ },
1605+ Transition {
1606+ from: "180"; to: "270"
1607+ enabled: d.animationType == d.fullAnimation
1608+ NinetyRotationAnimation { fromAngle: 180; toAngle: 270
1609+ info: d; shell: root.shell }
1610+ },
1611+ Transition {
1612+ from: "270"; to: "180"
1613+ enabled: d.animationType == d.fullAnimation
1614+ NinetyRotationAnimation { fromAngle: 270; toAngle: 180
1615+ info: d; shell: root.shell }
1616+ },
1617+ Transition {
1618+ from: "0"; to: "180"
1619+ enabled: d.animationType == d.fullAnimation
1620+ HalfLoopRotationAnimation { fromAngle: 0; toAngle: 180
1621+ info: d; shell: root.shell }
1622+ },
1623+ Transition {
1624+ from: "180"; to: "0"
1625+ enabled: d.animationType == d.fullAnimation
1626+ HalfLoopRotationAnimation { fromAngle: 180; toAngle: 0
1627+ info: d; shell: root.shell }
1628+ },
1629+ Transition {
1630+ from: "90"; to: "270"
1631+ enabled: d.animationType == d.fullAnimation
1632+ HalfLoopRotationAnimation { fromAngle: 90; toAngle: 270
1633+ info: d; shell: root.shell }
1634+ },
1635+ Transition {
1636+ from: "270"; to: "90"
1637+ enabled: d.animationType == d.fullAnimation
1638+ HalfLoopRotationAnimation { fromAngle: 270; toAngle: 90
1639+ info: d; shell: root.shell }
1640+ },
1641+ Transition {
1642+ objectName: "immediateTransition"
1643+ enabled: d.animationType == d.noAnimation
1644+ ImmediateRotationAction { info: d; shell: root.shell }
1645+ },
1646+ Transition {
1647+ enabled: d.animationType == d.indicatorsBarAnimation
1648+ SequentialAnimation {
1649+ ScriptAction { script: {
1650+ d.transitioning = true;
1651+ } }
1652+ NumberAnimation {
1653+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
1654+ target: root.shell; property: "indicatorAreaShowProgress"
1655+ from: 1.0; to: 0.0
1656+ }
1657+ ImmediateRotationAction { info: d; shell: root.shell }
1658+ NumberAnimation {
1659+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
1660+ target: root.shell; property: "indicatorAreaShowProgress"
1661+ from: 0.0; to: 1.0
1662+ }
1663+ ScriptAction { script: {
1664+ d.transitioning = false;
1665+ }}
1666+ }
1667+ }
1668+ ]
1669+
1670+}
1671
1672=== modified file 'qml/Shell.qml'
1673--- qml/Shell.qml 2015-04-09 14:01:00 +0000
1674+++ qml/Shell.qml 2015-04-16 12:11:39 +0000
1675@@ -1,5 +1,5 @@
1676 /*
1677- * Copyright (C) 2013 Canonical, Ltd.
1678+ * Copyright (C) 2013-2015 Canonical, Ltd.
1679 *
1680 * This program is free software; you can redistribute it and/or modify
1681 * it under the terms of the GNU General Public License as published by
1682@@ -45,40 +45,68 @@
1683 Item {
1684 id: shell
1685
1686+ // to be set from outside
1687+ property int orientationAngle: 0
1688+ property int orientation
1689+ property int primaryOrientation
1690+ property int nativeOrientation
1691+ property real nativeWidth
1692+ property real nativeHeight
1693+ property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
1694+ property bool beingResized
1695+ property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
1696+ function updateFocusedAppOrientation() {
1697+ applicationsDisplayLoader.item.updateFocusedAppOrientation();
1698+ }
1699+ function updateFocusedAppOrientationAnimated() {
1700+ applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
1701+ }
1702+
1703+ // to be read from outside
1704+ readonly property int mainAppWindowOrientationAngle:
1705+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
1706+
1707+ readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
1708+ && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
1709+ && !greeter.animating
1710+
1711+ property bool startingUp: true
1712+ Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
1713+
1714+ property int supportedOrientations: {
1715+ if (startingUp) {
1716+ // Ensure we don't rotate during start up
1717+ return Qt.PrimaryOrientation;
1718+ } else if (greeter.shown) {
1719+ return Qt.PrimaryOrientation;
1720+ } else if (mainApp) {
1721+ return mainApp.supportedOrientations;
1722+ } else {
1723+ // we just don't care
1724+ return Qt.PortraitOrientation
1725+ | Qt.LandscapeOrientation
1726+ | Qt.InvertedPortraitOrientation
1727+ | Qt.InvertedLandscapeOrientation;
1728+ }
1729+ }
1730+
1731+ // For autopilot consumption
1732+ readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
1733+
1734+ // internal props from here onwards
1735+ readonly property var mainApp:
1736+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
1737+
1738 // Disable everything while greeter is waiting, so that the user can't swipe
1739 // the greeter or launcher until we know whether the session is locked.
1740 enabled: !greeter.waiting
1741
1742- // this is only here to select the width / height of the window if not running fullscreen
1743- property bool tablet: false
1744- width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
1745- height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
1746-
1747 property real edgeSize: units.gu(2)
1748 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
1749 property url background: asImageTester.status == Image.Ready ? asImageTester.source
1750 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
1751 readonly property real panelHeight: panel.panelHeight
1752
1753- property bool sideStageEnabled: shell.width >= units.gu(100)
1754- readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
1755-
1756- property int orientation
1757- readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
1758- onDeviceOrientationAngleChanged: {
1759- if (!OrientationLock.enabled) {
1760- orientation = Screen.orientation;
1761- }
1762- }
1763- readonly property bool orientationLockEnabled: OrientationLock.enabled
1764- onOrientationLockEnabledChanged: {
1765- if (orientationLockEnabled) {
1766- OrientationLock.savedOrientation = Screen.orientation;
1767- } else {
1768- orientation = Screen.orientation;
1769- }
1770- }
1771-
1772 // This is _only_ used to expose the property to autopilot tests
1773 readonly property string testShellMode: shellMode
1774
1775@@ -86,7 +114,8 @@
1776 if (ApplicationManager.findApplication(appId)) {
1777 ApplicationManager.requestFocusApplication(appId);
1778 } else {
1779- var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
1780+ var execFlags = shell.usageScenario === "phone" ? ApplicationManager.ForceMainStage
1781+ : ApplicationManager.NoFlag;
1782 ApplicationManager.startApplication(appId, execFlags);
1783 }
1784 }
1785@@ -124,11 +153,6 @@
1786 sourceSize.width: 0
1787 }
1788
1789- GSettings {
1790- id: usageModeSettings
1791- schema.id: "com.canonical.Unity8"
1792- }
1793-
1794 Binding {
1795 target: LauncherModel
1796 property: "applicationManager"
1797@@ -140,9 +164,7 @@
1798 if (ApplicationManager.count > 0) {
1799 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
1800 }
1801- if (orientationLockEnabled) {
1802- orientation = OrientationLock.savedOrientation;
1803- }
1804+ finishStartUpTimer.start();
1805 }
1806
1807 VolumeControl {
1808@@ -231,9 +253,19 @@
1809 // theoretical attack where user enters lockedApp mode, then makes
1810 // the screen larger (maybe connects to monitor) and tries to enter
1811 // tablet mode.
1812- property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
1813- source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
1814- : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
1815+
1816+ property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
1817+ ? "phone"
1818+ : shell.usageScenario
1819+ source: {
1820+ if (applicationsDisplayLoader.usageScenario === "phone") {
1821+ return "Stages/PhoneStage.qml";
1822+ } else if (applicationsDisplayLoader.usageScenario === "tablet") {
1823+ return "Stages/TabletStage.qml";
1824+ } else {
1825+ return "Stages/DesktopStage.qml";
1826+ }
1827+ }
1828
1829 property bool interactive: tutorial.spreadEnabled
1830 && !greeter.shown
1831@@ -276,7 +308,12 @@
1832 }
1833 Binding {
1834 target: applicationsDisplayLoader.item
1835- property: "orientation"
1836+ property: "shellOrientationAngle"
1837+ value: shell.orientationAngle
1838+ }
1839+ Binding {
1840+ target: applicationsDisplayLoader.item
1841+ property: "shellOrientation"
1842 value: shell.orientation
1843 }
1844 Binding {
1845@@ -284,6 +321,31 @@
1846 property: "background"
1847 value: shell.background
1848 }
1849+ Binding {
1850+ target: applicationsDisplayLoader.item
1851+ property: "shellPrimaryOrientation"
1852+ value: shell.primaryOrientation
1853+ }
1854+ Binding {
1855+ target: applicationsDisplayLoader.item
1856+ property: "nativeOrientation"
1857+ value: shell.nativeOrientation
1858+ }
1859+ Binding {
1860+ target: applicationsDisplayLoader.item
1861+ property: "nativeWidth"
1862+ value: shell.nativeWidth
1863+ }
1864+ Binding {
1865+ target: applicationsDisplayLoader.item
1866+ property: "nativeHeight"
1867+ value: shell.nativeHeight
1868+ }
1869+ Binding {
1870+ target: applicationsDisplayLoader.item
1871+ property: "beingResized"
1872+ value: shell.beingResized
1873+ }
1874 }
1875
1876 Tutorial {
1877@@ -353,7 +415,7 @@
1878 objectName: "greeter"
1879
1880 hides: [launcher, panel.indicators]
1881- tabletMode: shell.sideStageEnabled
1882+ tabletMode: shell.usageScenario !== "phone"
1883 launcherOffset: launcher.progress
1884 forcedUnlock: tutorial.running
1885 background: shell.background
1886@@ -480,20 +542,18 @@
1887 expandedPanelHeight: units.gu(7)
1888
1889 indicatorsModel: Indicators.IndicatorsModel {
1890- // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
1891- profile: indicatorProfile
1892- Component.onCompleted: load()
1893+ // tablet and phone both use the same profile
1894+ profile: shell.usageScenario === "desktop" ? "desktop" : "phone"
1895+ Component.onCompleted: load();
1896 }
1897 }
1898+
1899 callHint {
1900 greeterShown: greeter.shown
1901 }
1902
1903- property bool topmostApplicationIsFullscreen:
1904- ApplicationManager.focusedApplicationId &&
1905- ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
1906-
1907- fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
1908+ property bool mainAppIsFullscreen: shell.mainApp && shell.mainApp.fullscreen
1909+ fullscreenMode: (mainAppIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
1910 || greeter.hasLockedApp
1911 }
1912
1913@@ -511,7 +571,7 @@
1914 available: tutorial.launcherEnabled
1915 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
1916 && !greeter.hasLockedApp
1917- inverted: usageModeSettings.usageMode === "Staged"
1918+ inverted: shell.usageScenario !== "desktop"
1919 shadeBackground: !tutorial.running
1920
1921 onShowDashHome: showHome()
1922@@ -553,7 +613,7 @@
1923 Rectangle {
1924 id: modalNotificationBackground
1925
1926- visible: notifications.useModal && (notifications.state == "narrow")
1927+ visible: notifications.useModal
1928 color: "#000000"
1929 anchors.fill: parent
1930 opacity: 0.9
1931@@ -598,6 +658,7 @@
1932
1933 Dialogs {
1934 id: dialogs
1935+ objectName: "dialogs"
1936 anchors.fill: parent
1937 z: overlay.z + 10
1938 onPowerOffClicked: {
1939
1940=== modified file 'qml/Stages/ApplicationWindow.qml'
1941--- qml/Stages/ApplicationWindow.qml 2015-02-26 22:35:53 +0000
1942+++ qml/Stages/ApplicationWindow.qml 2015-04-16 12:11:39 +0000
1943@@ -1,5 +1,5 @@
1944 /*
1945- * Copyright 2014 Canonical Ltd.
1946+ * Copyright 2014-2015 Canonical Ltd.
1947 *
1948 * This program is free software; you can redistribute it and/or modify
1949 * it under the terms of the GNU Lesser General Public License as published by
1950@@ -24,10 +24,11 @@
1951 // to be read from outside
1952 readonly property bool fullscreen: application ? application.fullscreen : false
1953 property alias interactive: sessionContainer.interactive
1954+ property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
1955
1956 // to be set from outside
1957 property QtObject application
1958- property int orientation
1959+ property int surfaceOrientationAngle
1960
1961 QtObject {
1962 id: d
1963@@ -62,6 +63,15 @@
1964 // Remove this when possible
1965 property bool surfaceInitialized: false
1966
1967+ property bool supportsSurfaceResize:
1968+ application &&
1969+ ((application.supportedOrientations & Qt.PortraitOrientation)
1970+ || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
1971+ &&
1972+ ((application.supportedOrientations & Qt.LandscapeOrientation)
1973+ || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
1974+
1975+ property bool surfaceOldEnoughToBeResized: false
1976 }
1977
1978 Timer {
1979@@ -70,6 +80,12 @@
1980 onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
1981 }
1982
1983+ Timer {
1984+ id: surfaceIsOldTimer
1985+ interval: 1000
1986+ onTriggered: { if (stateGroup.state === "surface") { d.surfaceOldEnoughToBeResized = true; } }
1987+ }
1988+
1989 Image {
1990 id: screenshotImage
1991 objectName: "screenshotImage"
1992@@ -113,7 +129,8 @@
1993 // A fake application might not even have a session property.
1994 session: application && application.session ? application.session : null
1995 anchors.fill: parent
1996- orientation: root.orientation
1997+
1998+ surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
1999
2000 onSurfaceChanged: {
2001 if (sessionContainer.surface) {
2002@@ -179,15 +196,21 @@
2003 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
2004 from: 0.0; to: 1.0
2005 duration: UbuntuAnimation.BriskDuration }
2006- PropertyAction { target: splashLoader; property: "active"; value: false }
2007+ ScriptAction { script: {
2008+ splashLoader.active = false;
2009+ surfaceIsOldTimer.start();
2010+ } }
2011 }
2012 },
2013 Transition {
2014 from: "surface"; to: "splashScreen"
2015 SequentialAnimation {
2016- PropertyAction { target: splashLoader; property: "active"; value: true }
2017- PropertyAction { target: sessionContainer.surfaceContainer
2018- property: "visible"; value: true }
2019+ ScriptAction { script: {
2020+ surfaceIsOldTimer.stop();
2021+ d.surfaceOldEnoughToBeResized = false;
2022+ splashLoader.active = true;
2023+ sessionContainer.surfaceContainer.visible = true;
2024+ } }
2025 UbuntuNumberAnimation { target: splashLoader; property: "opacity";
2026 from: 0.0; to: 1.0
2027 duration: UbuntuAnimation.BriskDuration }
2028@@ -198,14 +221,18 @@
2029 Transition {
2030 from: "surface"; to: "screenshot"
2031 SequentialAnimation {
2032- PropertyAction { target: screenshotImage
2033- property: "visible"; value: true }
2034+ ScriptAction { script: {
2035+ surfaceIsOldTimer.stop();
2036+ d.surfaceOldEnoughToBeResized = false;
2037+ screenshotImage.visible = true;
2038+ } }
2039 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
2040 from: 0.0; to: 1.0
2041 duration: UbuntuAnimation.BriskDuration }
2042- PropertyAction { target: sessionContainer.surfaceContainer
2043- property: "visible"; value: false }
2044- ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
2045+ ScriptAction { script: {
2046+ sessionContainer.surfaceContainer.visible = false;
2047+ if (sessionContainer.session) { sessionContainer.session.release(); }
2048+ } }
2049 }
2050 },
2051 Transition {
2052@@ -216,8 +243,11 @@
2053 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
2054 from: 1.0; to: 0.0
2055 duration: UbuntuAnimation.BriskDuration }
2056- PropertyAction { target: screenshotImage; property: "visible"; value: false }
2057- PropertyAction { target: screenshotImage; property: "source"; value: "" }
2058+ ScriptAction { script: {
2059+ screenshotImage.visible = false;
2060+ screenshotImage.source = "";
2061+ surfaceIsOldTimer.start();
2062+ } }
2063 }
2064 },
2065 Transition {
2066@@ -233,10 +263,12 @@
2067 },
2068 Transition {
2069 from: "surface"; to: "void"
2070- SequentialAnimation {
2071- PropertyAction { target: sessionContainer.surfaceContainer; property: "visible"; value: false }
2072- ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
2073- }
2074+ ScriptAction { script: {
2075+ surfaceIsOldTimer.stop();
2076+ d.surfaceOldEnoughToBeResized = false;
2077+ sessionContainer.surfaceContainer.visible = false;
2078+ if (sessionContainer.session) { sessionContainer.session.release(); }
2079+ } }
2080 },
2081 Transition {
2082 from: "void"; to: "surface"
2083@@ -246,6 +278,9 @@
2084 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
2085 from: 0.0; to: 1.0
2086 duration: UbuntuAnimation.BriskDuration }
2087+ ScriptAction { script: {
2088+ surfaceIsOldTimer.start();
2089+ } }
2090 }
2091 }
2092 ]
2093
2094=== modified file 'qml/Stages/DesktopStage.qml'
2095--- qml/Stages/DesktopStage.qml 2015-03-13 19:18:35 +0000
2096+++ qml/Stages/DesktopStage.qml 2015-04-16 12:11:39 +0000
2097@@ -27,6 +27,29 @@
2098
2099 anchors.fill: parent
2100
2101+ // Controls to be set from outside
2102+ property int dragAreaWidth // just to comply with the interface shared between stages
2103+ property real maximizedAppTopMargin
2104+ property bool interactive
2105+ property bool spreadEnabled // just to comply with the interface shared between stages
2106+ property real inverseProgress: 0 // just to comply with the interface shared between stages
2107+ property int shellOrientationAngle: 0
2108+ property int shellOrientation
2109+ property int shellPrimaryOrientation
2110+ property int nativeOrientation
2111+ property bool beingResized: false
2112+
2113+ // functions to be called from outside
2114+ function updateFocusedAppOrientation() { /* TODO */ }
2115+ function updateFocusedAppOrientationAnimated() { /* TODO */}
2116+
2117+ // To be read from outside
2118+ readonly property var mainApp: ApplicationManager.focusedApplicationId
2119+ ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
2120+ : null
2121+ property int mainAppWindowOrientationAngle: 0
2122+ readonly property bool orientationChangesEnabled: false
2123+
2124 property alias background: wallpaper.source
2125
2126 property var windowStateStorage: WindowStateStorage
2127
2128=== added file 'qml/Stages/OrientationChangeAnimation.qml'
2129--- qml/Stages/OrientationChangeAnimation.qml 1970-01-01 00:00:00 +0000
2130+++ qml/Stages/OrientationChangeAnimation.qml 2015-04-16 12:11:39 +0000
2131@@ -0,0 +1,243 @@
2132+/*
2133+ * Copyright 2015 Canonical Ltd.
2134+ *
2135+ * This program is free software; you can redistribute it and/or modify
2136+ * it under the terms of the GNU Lesser General Public License as published by
2137+ * the Free Software Foundation; version 3.
2138+ *
2139+ * This program is distributed in the hope that it will be useful,
2140+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2141+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2142+ * GNU Lesser General Public License for more details.
2143+ *
2144+ * You should have received a copy of the GNU Lesser General Public License
2145+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2146+ */
2147+
2148+import QtQuick 2.3
2149+
2150+QtObject {
2151+ id: root
2152+
2153+ // to be set from outside
2154+ property Item spreadDelegate
2155+ property Item background
2156+ property Item window
2157+ property Image screenshot
2158+
2159+ function start() {
2160+ if (window.orientationAngle === 0) {
2161+ if (spreadDelegate.shellOrientationAngle === 90) {
2162+ chosenAnimation = simple90Animation;
2163+ } else if (spreadDelegate.shellOrientationAngle === 180) {
2164+ chosenAnimation = halfLoopAnimation;
2165+ } else if (spreadDelegate.shellOrientationAngle === 270) {
2166+ chosenAnimation = moving90Animation;
2167+ } else {
2168+ chosenAnimation = null;
2169+ }
2170+ } else if (window.orientationAngle === 90) {
2171+ if (spreadDelegate.shellOrientationAngle === 0) {
2172+ chosenAnimation = simple90Animation;
2173+ } else if (spreadDelegate.shellOrientationAngle === 180) {
2174+ chosenAnimation = moving90Animation;
2175+ } else if (spreadDelegate.shellOrientationAngle === 270) {
2176+ chosenAnimation = halfLoopAnimation;
2177+ } else {
2178+ chosenAnimation = null;
2179+ }
2180+ } else if (window.orientationAngle === 180) {
2181+ if (spreadDelegate.shellOrientationAngle === 0) {
2182+ chosenAnimation = halfLoopAnimation;
2183+ } else if (spreadDelegate.shellOrientationAngle === 90) {
2184+ chosenAnimation = moving90Animation;
2185+ } else if (spreadDelegate.shellOrientationAngle === 270) {
2186+ chosenAnimation = simple90Animation;
2187+ } else {
2188+ chosenAnimation = null;
2189+ }
2190+ } else if (window.orientationAngle === 270) {
2191+ if (spreadDelegate.shellOrientationAngle === 0) {
2192+ chosenAnimation = moving90Animation;
2193+ } else if (spreadDelegate.shellOrientationAngle === 90) {
2194+ chosenAnimation = halfLoopAnimation;
2195+ } else if (spreadDelegate.shellOrientationAngle === 180) {
2196+ chosenAnimation = simple90Animation;
2197+ } else {
2198+ chosenAnimation = null;
2199+ }
2200+ }
2201+
2202+ if (chosenAnimation)
2203+ chosenAnimation.start();
2204+ }
2205+
2206+ // to be read from outside
2207+ property bool running: chosenAnimation ? chosenAnimation.running : false
2208+
2209+ property int duration: 450
2210+ property int easingType: Easing.InOutCubic
2211+ // Those values are good for debugging/development
2212+ //property int duration: 6000
2213+ //property int easingType: Easing.Linear
2214+
2215+ property int shortestDimension: spreadDelegate.width < spreadDelegate.height
2216+ ? spreadDelegate.width : spreadDelegate.height
2217+ property int longestDimension: spreadDelegate.width > spreadDelegate.height
2218+ ? spreadDelegate.width : spreadDelegate.height
2219+ property string longestAxis: spreadDelegate.width > spreadDelegate.height ? "x" : "y"
2220+
2221+ property QtObject chosenAnimation
2222+
2223+ function setup90Animation() {
2224+ background.visible = true;
2225+
2226+ screenshot.width = window.width
2227+ screenshot.height = window.height
2228+ screenshot.take();
2229+ screenshot.transformOriginX = root.shortestDimension / 2
2230+ screenshot.transformOriginY = root.shortestDimension / 2
2231+ screenshot.visible = true;
2232+
2233+ window.rotation = 0;
2234+ window.width = spreadDelegate.width;
2235+ window.height = spreadDelegate.height;
2236+ window.transformOriginX = root.shortestDimension / 2
2237+ window.transformOriginY = root.shortestDimension / 2
2238+ }
2239+
2240+ function tearDown90Animation() {
2241+ window.orientationAngle = spreadDelegate.shellOrientationAngle;
2242+ screenshot.source = "";
2243+ screenshot.visible = false;
2244+ background.visible = false;
2245+ }
2246+
2247+ property QtObject simple90Animation: SequentialAnimation {
2248+ id: simple90Animation
2249+
2250+ ScriptAction { script: setup90Animation() }
2251+ ParallelAnimation {
2252+ RotationAnimation {
2253+ target: root.window
2254+ duration: root.duration
2255+ easing.type: root.easingType
2256+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
2257+ to: 0
2258+ property: "transformRotationAngle"
2259+ }
2260+ RotationAnimation {
2261+ target: root.screenshot
2262+ duration: root.duration
2263+ easing.type: root.easingType
2264+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
2265+ to: 0
2266+ property: "transformRotationAngle"
2267+ }
2268+ NumberAnimation {
2269+ target: root.screenshot
2270+ duration: root.duration
2271+ easing.type: root.easingType
2272+ property: "opacity"
2273+ from: 1.0
2274+ to: 0.0
2275+ }
2276+ NumberAnimation {
2277+ target: root.window
2278+ duration: root.duration
2279+ easing.type: root.easingType
2280+ property: "opacity"
2281+ from: 0.0
2282+ to: 1.0
2283+ }
2284+ }
2285+ ScriptAction { script: tearDown90Animation() }
2286+ }
2287+
2288+ property QtObject moving90Animation: SequentialAnimation {
2289+ id: moving90Animation
2290+
2291+ ScriptAction { script: setup90Animation() }
2292+ ParallelAnimation {
2293+ RotationAnimation {
2294+ target: root.window
2295+ duration: root.duration
2296+ easing.type: root.easingType
2297+ direction: RotationAnimation.Shortest
2298+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
2299+ to: 0
2300+ property: "transformRotationAngle"
2301+ }
2302+ RotationAnimation {
2303+ target: root.screenshot
2304+ duration: root.duration
2305+ easing.type: root.easingType
2306+ direction: RotationAnimation.Shortest
2307+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
2308+ to: 0
2309+ property: "transformRotationAngle"
2310+ }
2311+ NumberAnimation {
2312+ target: root.screenshot
2313+ duration: root.duration
2314+ easing.type: root.easingType
2315+ property: "opacity"
2316+ from: 1.0
2317+ to: 0.0
2318+ }
2319+ NumberAnimation {
2320+ target: root.window
2321+ duration: root.duration
2322+ easing.type: root.easingType
2323+ property: "opacity"
2324+ from: 0.0
2325+ to: 1.0
2326+ }
2327+ NumberAnimation {
2328+ target: root.window
2329+ duration: root.duration
2330+ easing.type: root.easingType
2331+ property: root.longestAxis
2332+ from: root.longestDimension - root.shortestDimension
2333+ to: 0
2334+ }
2335+ NumberAnimation {
2336+ target: root.screenshot
2337+ duration: root.duration
2338+ easing.type: root.easingType
2339+ property: root.longestAxis
2340+ from: root.longestDimension - root.shortestDimension
2341+ to: 0
2342+ }
2343+ }
2344+ ScriptAction { script: tearDown90Animation() }
2345+ }
2346+
2347+ property QtObject halfLoopAnimation: SequentialAnimation {
2348+ id: halfLoopAnimation
2349+
2350+ ScriptAction { script: {
2351+ background.visible = true;
2352+
2353+ window.rotation = 0;
2354+ window.width = spreadDelegate.width;
2355+ window.height = spreadDelegate.height;
2356+ window.transformOriginX = window.width / 2
2357+ window.transformOriginY = window.height / 2
2358+ } }
2359+ ParallelAnimation {
2360+ RotationAnimation {
2361+ target: root.window
2362+ duration: root.duration
2363+ easing.type: root.easingType
2364+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
2365+ to: 0
2366+ property: "transformRotationAngle"
2367+ }
2368+ }
2369+ ScriptAction { script: {
2370+ window.orientationAngle = spreadDelegate.shellOrientationAngle;
2371+ background.visible = false;
2372+ } }
2373+ }
2374+}
2375
2376=== modified file 'qml/Stages/PhoneStage.qml'
2377--- qml/Stages/PhoneStage.qml 2015-02-18 18:29:03 +0000
2378+++ qml/Stages/PhoneStage.qml 2015-04-16 12:11:39 +0000
2379@@ -1,5 +1,5 @@
2380 /*
2381- * Copyright (C) 2014 Canonical, Ltd.
2382+ * Copyright (C) 2014-2015 Canonical, Ltd.
2383 *
2384 * This program is free software; you can redistribute it and/or modify
2385 * it under the terms of the GNU General Public License as published by
2386@@ -31,12 +31,64 @@
2387 property bool interactive
2388 property bool spreadEnabled: true // If false, animations and right edge will be disabled
2389 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
2390- property int orientation: Qt.PortraitOrientation
2391 property QtObject applicationManager: ApplicationManager
2392 property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
2393 property bool altTabEnabled: true
2394 property real startScale: 1.1
2395 property real endScale: 0.7
2396+ property int shellOrientationAngle: 0
2397+ property int shellOrientation
2398+ property int shellPrimaryOrientation
2399+ property int nativeOrientation
2400+ property real nativeWidth
2401+ property real nativeHeight
2402+ property bool beingResized: false
2403+ onBeingResizedChanged: {
2404+ if (beingResized) {
2405+ // Brace yourselves for impact!
2406+ spreadView.selectedIndex = -1;
2407+ spreadView.phase = 0;
2408+ spreadView.contentX = -spreadView.shift;
2409+ }
2410+ }
2411+ function updateFocusedAppOrientation() {
2412+ if (spreadRepeater.count > 0) {
2413+ spreadRepeater.itemAt(0).matchShellOrientation();
2414+ }
2415+
2416+ for (var i = 1; i < spreadRepeater.count; ++i) {
2417+
2418+ var spreadDelegate = spreadRepeater.itemAt(i);
2419+
2420+ var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
2421+ if (delta < 0) { delta += 360; }
2422+ delta = delta % 360;
2423+
2424+ var supportedOrientations = spreadDelegate.application.supportedOrientations;
2425+ if (supportedOrientations === Qt.PrimaryOrientation) {
2426+ supportedOrientations = spreadDelegate.shellPrimaryOrientation;
2427+ }
2428+
2429+ if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
2430+ spreadDelegate.matchShellOrientation();
2431+ }
2432+ }
2433+ }
2434+ function updateFocusedAppOrientationAnimated() {
2435+ if (spreadRepeater.count > 0) {
2436+ spreadRepeater.itemAt(0).animateToShellOrientation();
2437+ }
2438+ }
2439+
2440+ // To be read from outside
2441+ readonly property var mainApp: applicationManager.focusedApplicationId
2442+ ? applicationManager.findApplication(applicationManager.focusedApplicationId)
2443+ : null
2444+ property int mainAppWindowOrientationAngle: 0
2445+ readonly property bool orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
2446+ && !priv.focusedAppDelegateIsDislocated
2447+ && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
2448+ && spreadView.phase === 0
2449
2450 // How far left the stage has been dragged
2451 readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
2452@@ -54,12 +106,6 @@
2453 spreadView.snapTo(priv.indexOf(appId));
2454 }
2455
2456- onWidthChanged: {
2457- spreadView.selectedIndex = -1;
2458- spreadView.phase = 0;
2459- spreadView.contentX = -spreadView.shift;
2460- }
2461-
2462 onInverseProgressChanged: {
2463 // This can't be a simple binding because that would be triggered after this handler
2464 // while we need it active before doing the anition left/right
2465@@ -73,6 +119,20 @@
2466 priv.oldInverseProgress = inverseProgress;
2467 }
2468
2469+ // <FIXME-contentX> See rationale in the next comment with this tag
2470+ onWidthChanged: {
2471+ if (!root.beingResized) {
2472+ // we're being resized without a warning (ie, the corresponding property wasn't set
2473+ root.beingResized = true;
2474+ beingResizedTimer.start();
2475+ }
2476+ }
2477+ Timer {
2478+ id: beingResizedTimer
2479+ interval: 100
2480+ onTriggered: { root.beingResized = false; }
2481+ }
2482+
2483 Connections {
2484 target: applicationManager
2485
2486@@ -115,10 +175,10 @@
2487 QtObject {
2488 id: priv
2489
2490- readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
2491- property string focusedAppId: applicationManager.focusedApplicationId
2492- property var focusedApplication: applicationManager.findApplication(focusedAppId)
2493+ property string focusedAppId: root.applicationManager.focusedApplicationId
2494+ property var focusedApplication: root.applicationManager.findApplication(focusedAppId)
2495 property var focusedAppDelegate: null
2496+ property bool focusedAppOrientationChangesEnabled: false
2497
2498 property real oldInverseProgress: 0
2499 property bool animateX: false
2500@@ -131,15 +191,27 @@
2501 }
2502 }
2503
2504+ property bool focusedAppDelegateIsDislocated: focusedAppDelegate && focusedAppDelegate.x !== 0
2505+
2506 function indexOf(appId) {
2507- for (var i = 0; i < applicationManager.count; i++) {
2508- if (applicationManager.get(i).appId == appId) {
2509+ for (var i = 0; i < root.applicationManager.count; i++) {
2510+ if (root.applicationManager.get(i).appId == appId) {
2511 return i;
2512 }
2513 }
2514 return -1;
2515 }
2516
2517+ // Is more stable than "spreadView.shiftedContentX === 0" as it filters out noise caused by
2518+ // Flickable.contentX changing due to resizes.
2519+ property bool fullyShowingFocusedApp: true
2520+ }
2521+ Timer {
2522+ id: fullyShowingFocusedAppUpdateTimer
2523+ interval: 100
2524+ onTriggered: {
2525+ priv.fullyShowingFocusedApp = spreadView.shiftedContentX === 0;
2526+ }
2527 }
2528
2529 Flickable {
2530@@ -153,7 +225,9 @@
2531
2532 // This indicates when the spreadView is active. That means, all the animations
2533 // are activated and tiles need to line up for the spread.
2534- readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
2535+ readonly property bool active: shiftedContentX > 0
2536+ || spreadDragArea.status === DirectionalDragArea.Recognized
2537+ || !root.focusFirstApp
2538
2539 // The flickable needs to fill the screen in order to get touch events all over.
2540 // However, we don't want to the user to be able to scroll back all the way. For
2541@@ -188,9 +262,25 @@
2542 property int draggedDelegateCount: 0
2543 property int closingIndex: -1
2544
2545- property bool focusChanging: false
2546+ // <FIXME-contentX> Workaround Flickable's behavior of bringing contentX back between valid boundaries
2547+ // when resized. The proper way to fix this is refactoring PhoneStage so that it doesn't
2548+ // rely on having Flickable.contentX keeping an out-of-bounds value when it's set programatically
2549+ // (as opposed to having contentX reaching an out-of-bounds value through dragging, which will trigger
2550+ // the Flickable.boundsBehavior upon release).
2551+ onContentXChanged: { forceItToRemainStillIfBeingResized(); }
2552+ onShiftChanged: { forceItToRemainStillIfBeingResized(); }
2553+ function forceItToRemainStillIfBeingResized() {
2554+ if (root.beingResized && contentX != -spreadView.shift) {
2555+ contentX = -spreadView.shift;
2556+ }
2557+ }
2558
2559 onShiftedContentXChanged: {
2560+ if (root.beingResized) {
2561+ // Flickabe.contentX wiggles during resizes. Don't react to it.
2562+ return;
2563+ }
2564+
2565 switch (phase) {
2566 case 0:
2567 if (shiftedContentX > width * positionMarker2) {
2568@@ -205,6 +295,7 @@
2569 }
2570 break;
2571 }
2572+ fullyShowingFocusedAppUpdateTimer.restart();
2573 }
2574
2575 function snap() {
2576@@ -229,7 +320,7 @@
2577 snapAnimation.start();
2578 return;
2579 }
2580- if (applicationManager.count <= index) {
2581+ if (root.applicationManager.count <= index) {
2582 // In case we're trying to snap to some non existing app, lets snap back to the first one
2583 index = 0;
2584 }
2585@@ -266,7 +357,7 @@
2586 ScriptAction {
2587 script: {
2588 if (spreadView.selectedIndex >= 0) {
2589- applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
2590+ root.applicationManager.focusApplication(root.applicationManager.get(spreadView.selectedIndex).appId);
2591
2592 spreadView.selectedIndex = -1;
2593 spreadView.phase = 0;
2594@@ -281,7 +372,7 @@
2595 // This width controls how much the spread can be flicked left/right. It's composed of:
2596 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
2597 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
2598- width: Math.max(3, applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
2599+ width: Math.max(3, root.applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
2600 height: parent.height
2601 Behavior on width {
2602 enabled: spreadView.closingIndex >= 0
2603@@ -304,7 +395,7 @@
2604 Repeater {
2605 id: spreadRepeater
2606 objectName: "spreadRepeater"
2607- model: applicationManager
2608+ model: root.applicationManager
2609 delegate: TransformedSpreadDelegate {
2610 id: appDelegate
2611 objectName: "appDelegate" + index
2612@@ -319,11 +410,10 @@
2613 selected: spreadView.selectedIndex == index
2614 otherSelected: spreadView.selectedIndex >= 0 && !selected
2615 interactive: !spreadView.interactive && spreadView.phase === 0
2616- && spreadView.shiftedContentX === 0 && root.interactive && isFocused
2617+ && priv.fullyShowingFocusedApp && root.interactive && isFocused
2618 swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
2619 maximizedAppTopMargin: root.maximizedAppTopMargin
2620- dropShadow: spreadView.active ||
2621- (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
2622+ dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
2623 focusFirstApp: root.focusFirstApp
2624
2625 readonly property bool isDash: model.appId == "unity8-dash"
2626@@ -346,7 +436,7 @@
2627 return spreadView.width + spreadIndex * spreadView.tileDistance;
2628 }
2629
2630- application: applicationManager.get(index)
2631+ application: root.applicationManager.get(index)
2632 closeable: !isDash
2633
2634 property real behavioredIndex: index
2635@@ -362,18 +452,16 @@
2636 }
2637 }
2638
2639+ property var xBehavior: xBehavior
2640 Behavior on x {
2641+ id: xBehavior
2642 enabled: root.spreadEnabled &&
2643 !spreadView.active &&
2644 !snapAnimation.running &&
2645- priv.animateX
2646+ priv.animateX &&
2647+ !root.beingResized
2648 UbuntuNumberAnimation {
2649 duration: UbuntuAnimation.BriskDuration
2650- onRunningChanged: {
2651- if (!running && root.inverseProgress == 0) {
2652- spreadView.focusChanging = false;
2653- }
2654- }
2655 }
2656 }
2657
2658@@ -413,22 +501,21 @@
2659 }
2660
2661 // Hiding tiles when their progress is negative or reached the maximum
2662- visible: (progress >= 0 && progress < 1.7) ||
2663- (isDash && priv.focusedAppDelegate.x !== 0)
2664-
2665- Binding {
2666- target: appDelegate
2667- property: "orientation"
2668- when: appDelegate.interactive
2669- value: root.orientation
2670- }
2671+ visible: (progress >= 0 && progress < 1.7)
2672+ || (isDash && priv.focusedAppDelegateIsDislocated)
2673+
2674+
2675+ shellOrientationAngle: root.shellOrientationAngle
2676+ shellOrientation: root.shellOrientation
2677+ shellPrimaryOrientation: root.shellPrimaryOrientation
2678+ nativeOrientation: root.nativeOrientation
2679
2680 onClicked: {
2681 if (root.altTabEnabled && spreadView.phase == 2) {
2682- if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
2683+ if (root.applicationManager.focusedApplicationId == root.applicationManager.get(index).appId) {
2684 spreadView.snapTo(index);
2685 } else {
2686- applicationManager.requestFocusApplication(applicationManager.get(index).appId);
2687+ root.applicationManager.requestFocusApplication(root.applicationManager.get(index).appId);
2688 }
2689 }
2690 }
2691@@ -443,7 +530,20 @@
2692
2693 onClosed: {
2694 spreadView.closingIndex = index;
2695- applicationManager.stopApplication(applicationManager.get(index).appId);
2696+ root.applicationManager.stopApplication(root.applicationManager.get(index).appId);
2697+ }
2698+
2699+ Binding {
2700+ target: root
2701+ when: index == 0
2702+ property: "mainAppWindowOrientationAngle"
2703+ value: appWindowOrientationAngle
2704+ }
2705+ Binding {
2706+ target: priv
2707+ when: index == 0
2708+ property: "focusedAppOrientationChangesEnabled"
2709+ value: orientationChangesEnabled
2710 }
2711 }
2712 }
2713
2714=== modified file 'qml/Stages/SessionContainer.qml'
2715--- qml/Stages/SessionContainer.qml 2015-01-28 12:59:21 +0000
2716+++ qml/Stages/SessionContainer.qml 2015-04-16 12:11:39 +0000
2717@@ -1,5 +1,5 @@
2718 /*
2719- * Copyright 2014 Canonical Ltd.
2720+ * Copyright 2014-2015 Canonical Ltd.
2721 *
2722 * This program is free software; you can redistribute it and/or modify
2723 * it under the terms of the GNU Lesser General Public License as published by
2724@@ -24,14 +24,13 @@
2725 readonly property var childSessions: session ? session.childSessions : null
2726 readonly property alias surface: _surfaceContainer.surface
2727 property alias interactive: _surfaceContainer.interactive
2728- property int orientation
2729+ property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
2730
2731 readonly property alias surfaceContainer: _surfaceContainer
2732 SurfaceContainer {
2733 id: _surfaceContainer
2734 anchors.fill: parent
2735 surface: session ? session.surface : null
2736- orientation: root.orientation
2737 }
2738
2739 Repeater {
2740@@ -72,11 +71,6 @@
2741 target: item; when: item
2742 property: "height"; value: root.height
2743 }
2744-
2745- Binding {
2746- target: item; when: item
2747- property: "orientation"; value: root.orientation
2748- }
2749 }
2750 }
2751
2752
2753=== modified file 'qml/Stages/SpreadDelegate.qml'
2754--- qml/Stages/SpreadDelegate.qml 2015-01-28 12:59:21 +0000
2755+++ qml/Stages/SpreadDelegate.qml 2015-04-16 12:11:39 +0000
2756@@ -1,5 +1,5 @@
2757 /*
2758- * Copyright 2014 Canonical Ltd.
2759+ * Copyright 2014-2015 Canonical Ltd.
2760 *
2761 * This program is free software; you can redistribute it and/or modify
2762 * it under the terms of the GNU Lesser General Public License as published by
2763@@ -18,6 +18,7 @@
2764 */
2765
2766 import QtQuick 2.0
2767+import QtQuick.Window 2.0
2768 import Ubuntu.Components 1.1
2769 import "../Components"
2770
2771@@ -28,6 +29,8 @@
2772 readonly property bool dragged: dragArea.moving
2773 signal clicked()
2774 signal closed()
2775+ readonly property alias appWindowOrientationAngle: appWindowWithShadow.orientationAngle
2776+ readonly property alias orientationChangesEnabled: appWindow.orientationChangesEnabled
2777
2778 // to be set from outside
2779 property bool interactive: true
2780@@ -36,10 +39,54 @@
2781 property alias swipeToCloseEnabled: dragArea.enabled
2782 property bool closeable
2783 property alias application: appWindow.application
2784- property int orientation
2785+ property int shellOrientationAngle
2786+ property int shellOrientation
2787+ property int shellPrimaryOrientation
2788+ property int nativeOrientation
2789+
2790+ function matchShellOrientation() {
2791+ if (!root.application)
2792+ return;
2793+ appWindowWithShadow.orientationAngle = root.shellOrientationAngle;
2794+ }
2795+
2796+ function animateToShellOrientation() {
2797+ if (!root.application)
2798+ return;
2799+
2800+ if (root.application.rotatesWindowContents) {
2801+ appWindowWithShadow.orientationAngle = root.shellOrientationAngle;
2802+ } else {
2803+ orientationChangeAnimation.start();
2804+ }
2805+ }
2806+
2807+ OrientationChangeAnimation {
2808+ id: orientationChangeAnimation
2809+ objectName: "orientationChangeAnimation"
2810+ spreadDelegate: root
2811+ background: background
2812+ window: appWindowWithShadow
2813+ screenshot: appWindowScreenshot
2814+ }
2815+
2816+ QtObject {
2817+ id: priv
2818+ property bool startingUp: true
2819+ }
2820+
2821+ Component.onCompleted: { finishStartUpTimer.start(); }
2822+ Timer { id: finishStartUpTimer; interval: 400; onTriggered: priv.startingUp = false }
2823+
2824+ Rectangle {
2825+ id: background
2826+ color: "black"
2827+ anchors.fill: parent
2828+ visible: false
2829+ }
2830
2831 Item {
2832- objectName: "appWindowWithShadow"
2833+ objectName: "displacedAppWindowWithShadow"
2834
2835 readonly property real limit: root.height / 4
2836
2837@@ -52,28 +99,198 @@
2838 return k * (1 - Math.pow((k - 1) / k, distance))
2839 }
2840
2841- BorderImage {
2842- anchors {
2843- fill: appWindow
2844- margins: -units.gu(2)
2845- }
2846- source: "graphics/dropshadow2gu.sci"
2847- opacity: root.dropShadow ? .3 : 0
2848- Behavior on opacity { UbuntuNumberAnimation {} }
2849- }
2850-
2851- ApplicationWindow {
2852- id: appWindow
2853- objectName: application ? "appWindow_" + application.appId : "appWindow_null"
2854- focus: true
2855- anchors {
2856- fill: parent
2857- topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
2858- }
2859-
2860- interactive: root.interactive
2861- orientation: root.orientation
2862- }
2863+ Item {
2864+ id: appWindowWithShadow
2865+ objectName: "appWindowWithShadow"
2866+
2867+ property int orientationAngle
2868+
2869+ property real transformRotationAngle: 0
2870+ property real transformOriginX
2871+ property real transformOriginY
2872+
2873+ transform: Rotation {
2874+ origin.x: appWindowWithShadow.transformOriginX
2875+ origin.y: appWindowWithShadow.transformOriginY
2876+ axis { x: 0; y: 0; z: 1 }
2877+ angle: appWindowWithShadow.transformRotationAngle
2878+ }
2879+
2880+ state: {
2881+ if (priv.startingUp) {
2882+ return "startingUp";
2883+ } else if (root.application && root.application.rotatesWindowContents) {
2884+ return "counterRotate";
2885+ } else if (orientationChangeAnimation.running) {
2886+ return "animatingRotation";
2887+ } else {
2888+ return "keepSceneRotation";
2889+ }
2890+ }
2891+
2892+ states: [
2893+ // Sets the initial orientationAngle of the window, when it first slides into view
2894+ // (with the splash screen likely being displayed). At that point we just try to
2895+ // match shell's current orientation. We need a bit of time in this state as the
2896+ // information we need to decide orientationAngle may take a few cycles to
2897+ // be set.
2898+ State {
2899+ name: "startingUp"
2900+ PropertyChanges {
2901+ target: appWindowWithShadow
2902+ restoreEntryValues: false
2903+ orientationAngle: {
2904+ if (!root.application || root.application.rotatesWindowContents) {
2905+ return 0;
2906+ }
2907+ var supportedOrientations = root.application.supportedOrientations;
2908+
2909+ if (supportedOrientations === Qt.PrimaryOrientation) {
2910+ supportedOrientations = root.shellPrimaryOrientation;
2911+ }
2912+
2913+ // If it doesn't support shell's current orientation
2914+ // then simply pick some arbitraty one that it does support
2915+ var chosenOrientation = 0;
2916+ if (supportedOrientations & root.shellOrientation) {
2917+ chosenOrientation = root.shellOrientation;
2918+ } else if (supportedOrientations & Qt.PortraitOrientation) {
2919+ chosenOrientation = Qt.PortraitOrientation;
2920+ } else if (supportedOrientations & Qt.LandscapeOrientation) {
2921+ chosenOrientation = Qt.LandscapeOrientation;
2922+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
2923+ chosenOrientation = Qt.InvertedPortraitOrientation;
2924+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
2925+ chosenOrientation = Qt.InvertedLandscapeOrientation;
2926+ } else {
2927+ chosenOrientation = root.shellPrimaryOrientation;
2928+ }
2929+
2930+ return Screen.angleBetween(root.nativeOrientation, chosenOrientation);
2931+ }
2932+
2933+ rotation: appWindowWithShadow.orientationAngle - root.shellOrientationAngle
2934+ width: {
2935+ if (rotation == 0 || Math.abs(rotation) == 180) {
2936+ return root.width;
2937+ } else {
2938+ return root.height;
2939+ }
2940+ }
2941+ height: {
2942+ if (rotation == 0 || Math.abs(rotation) == 180)
2943+ return root.height;
2944+ else
2945+ return root.width;
2946+ }
2947+ }
2948+ },
2949+ // In this state we stick to our currently set orientationAngle, which may change only due
2950+ // to calls made to matchShellOrientation() or animateToShellOrientation()
2951+ State {
2952+ id: keepSceneRotationState
2953+ name: "keepSceneRotation"
2954+
2955+ StateChangeScript { script: {
2956+ // break binding
2957+ appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
2958+ } }
2959+ PropertyChanges {
2960+ target: appWindowWithShadow
2961+ restoreEntryValues: false
2962+ rotation: appWindowWithShadow.orientationAngle - root.shellOrientationAngle
2963+ width: {
2964+ if (rotation == 0 || Math.abs(rotation) == 180) {
2965+ return root.width;
2966+ } else {
2967+ return root.height;
2968+ }
2969+ }
2970+ height: {
2971+ if (rotation == 0 || Math.abs(rotation) == 180)
2972+ return root.height;
2973+ else
2974+ return root.width;
2975+ }
2976+ }
2977+ },
2978+ // In this state we counteract any shell rotation so that the window, in scene coordinates,
2979+ // remains unrotated.
2980+ State {
2981+ name: "counterRotate"
2982+ StateChangeScript { script: {
2983+ // break binding
2984+ appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
2985+ } }
2986+ PropertyChanges {
2987+ target: appWindowWithShadow
2988+ width: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.width : root.height
2989+ height: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.height : root.width
2990+ rotation: -root.shellOrientationAngle
2991+ }
2992+ PropertyChanges {
2993+ target: appWindow
2994+ surfaceOrientationAngle: orientationAngle
2995+ }
2996+ },
2997+ State {
2998+ name: "animatingRotation"
2999+ }
3000+ ]
3001+
3002+ x: (parent.width - width) / 2
3003+ y: (parent.height - height) / 2
3004+
3005+ BorderImage {
3006+ anchors {
3007+ fill: appWindow
3008+ margins: -units.gu(2)
3009+ }
3010+ source: "graphics/dropshadow2gu.sci"
3011+ opacity: root.dropShadow ? .3 : 0
3012+ Behavior on opacity { UbuntuNumberAnimation {} }
3013+ }
3014+
3015+ ApplicationWindow {
3016+ id: appWindow
3017+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
3018+ focus: true
3019+ anchors {
3020+ fill: parent
3021+ topMargin: appWindow.fullscreen || application.rotatesWindowContents
3022+ ? 0 : maximizedAppTopMargin
3023+ }
3024+
3025+ interactive: root.interactive
3026+ }
3027+ }
3028+ }
3029+
3030+ Image {
3031+ id: appWindowScreenshot
3032+ source: ""
3033+ visible: false
3034+
3035+ property real transformRotationAngle: 0
3036+ property real transformOriginX
3037+ property real transformOriginY
3038+
3039+ transform: Rotation {
3040+ origin.x: appWindowScreenshot.transformOriginX
3041+ origin.y: appWindowScreenshot.transformOriginY
3042+ axis { x: 0; y: 0; z: 1 }
3043+ angle: appWindowScreenshot.transformRotationAngle
3044+ }
3045+
3046+ function take() {
3047+ // Format: "image://application/$APP_ID/$CURRENT_TIME_MS"
3048+ // eg: "image://application/calculator-app/123456"
3049+ var timeMs = new Date().getTime();
3050+ source = "image://application/" + root.application.appId + "/" + timeMs;
3051+ }
3052+
3053+ sourceSize.width: width
3054+ sourceSize.height: height
3055 }
3056
3057 DraggingArea {
3058
3059=== modified file 'qml/Stages/SurfaceContainer.qml'
3060--- qml/Stages/SurfaceContainer.qml 2015-03-12 18:55:52 +0000
3061+++ qml/Stages/SurfaceContainer.qml 2015-04-16 12:11:39 +0000
3062@@ -24,8 +24,8 @@
3063 objectName: "surfaceContainer"
3064 property Item surface: null
3065 property bool hadSurface: false
3066- property int orientation
3067 property bool interactive
3068+ property int surfaceOrientationAngle: 0
3069
3070 onSurfaceChanged: {
3071 if (surface) {
3072@@ -39,10 +39,10 @@
3073 }
3074 }
3075 Binding { target: surface; property: "anchors.fill"; value: root }
3076- Binding { target: surface; property: "orientation"; value: root.orientation }
3077 Binding { target: surface; property: "z"; value: 1 }
3078 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
3079 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
3080+ Binding { target: surface; property: "orientationAngle"; value: root.surfaceOrientationAngle; when: surface }
3081
3082 InputWatcher {
3083 target: root.surface
3084
3085=== modified file 'qml/Stages/TabletStage.qml'
3086--- qml/Stages/TabletStage.qml 2015-02-02 16:28:03 +0000
3087+++ qml/Stages/TabletStage.qml 2015-04-16 12:11:39 +0000
3088@@ -1,5 +1,5 @@
3089 /*
3090- * Copyright (C) 2014 Canonical, Ltd.
3091+ * Copyright (C) 2014-2015 Canonical, Ltd.
3092 *
3093 * This program is free software; you can redistribute it and/or modify
3094 * it under the terms of the GNU General Public License as published by
3095@@ -28,13 +28,79 @@
3096 color: "#111111"
3097
3098 // Controls to be set from outside
3099- property bool shown: false
3100- property bool moving: false
3101 property int dragAreaWidth
3102 property real maximizedAppTopMargin
3103 property bool interactive
3104+ property alias beingResized: spreadView.beingResized
3105+
3106+ property bool spreadEnabled: true // If false, animations and right edge will be disabled
3107+
3108 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
3109- property int orientation: Qt.PortraitOrientation
3110+ property int shellOrientationAngle: 0
3111+ property int shellOrientation
3112+ property int shellPrimaryOrientation
3113+ property int nativeOrientation
3114+ property real nativeWidth
3115+ property real nativeHeight
3116+ function updateFocusedAppOrientation() {
3117+ var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
3118+ if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
3119+ spreadRepeater.itemAt(mainStageAppIndex).matchShellOrientation();
3120+ }
3121+
3122+ for (var i = 0; i < spreadRepeater.count; ++i) {
3123+
3124+ if (i === mainStageAppIndex) {
3125+ continue;
3126+ }
3127+
3128+ var spreadDelegate = spreadRepeater.itemAt(i);
3129+
3130+ var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
3131+ if (delta < 0) { delta += 360; }
3132+ delta = delta % 360;
3133+
3134+ var supportedOrientations = spreadDelegate.application.supportedOrientations;
3135+ if (supportedOrientations === Qt.PrimaryOrientation) {
3136+ supportedOrientations = spreadDelegate.shellPrimaryOrientation;
3137+ }
3138+
3139+ if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
3140+ spreadDelegate.matchShellOrientation();
3141+ }
3142+ }
3143+ }
3144+ function updateFocusedAppOrientationAnimated() {
3145+ var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
3146+ if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
3147+ spreadRepeater.itemAt(mainStageAppIndex).animateToShellOrientation();
3148+ }
3149+
3150+ if (priv.sideStageAppId) {
3151+ var sideStageAppIndex = priv.indexOf(priv.sideStageAppId);
3152+ if (sideStageAppIndex >= 0 && sideStageAppIndex < spreadRepeater.count) {
3153+ spreadRepeater.itemAt(sideStageAppIndex).matchShellOrientation();
3154+ }
3155+ }
3156+ }
3157+
3158+ // To be read from outside
3159+ property var mainApp: null
3160+ property int mainAppWindowOrientationAngle: 0
3161+ readonly property bool orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
3162+
3163+ onWidthChanged: {
3164+ spreadView.selectedIndex = -1;
3165+ spreadView.phase = 0;
3166+ spreadView.contentX = -spreadView.shift;
3167+ }
3168+
3169+ onShellOrientationChanged: {
3170+ if (shellOrientation == Qt.PortraitOrientation || shellOrientation == Qt.InvertedPortraitOrientation) {
3171+ ApplicationManager.focusApplication(priv.mainStageAppId);
3172+ priv.sideStageAppId = "";
3173+ }
3174+ }
3175
3176 onInverseProgressChanged: {
3177 // This can't be a simple binding because that would be triggered after this handler
3178@@ -54,6 +120,13 @@
3179
3180 property string focusedAppId: ApplicationManager.focusedApplicationId
3181 property string oldFocusedAppId: ""
3182+ property bool mainAppOrientationChangesEnabled: false
3183+
3184+ property real landscapeHeight: root.nativeOrientation == Qt.LandscapeOrientation ?
3185+ root.nativeHeight : root.nativeWidth
3186+
3187+ property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation
3188+ || root.shellOrientation === Qt.InvertedLandscapeOrientation
3189
3190 property string mainStageAppId
3191 property string sideStageAppId
3192@@ -71,6 +144,7 @@
3193 priv.sideStageAppId = focusedAppId;
3194 } else {
3195 priv.mainStageAppId = focusedAppId;
3196+ root.mainApp = focusedApp;
3197 }
3198 }
3199
3200@@ -169,6 +243,7 @@
3201
3202 Flickable {
3203 id: spreadView
3204+ objectName: "spreadView"
3205 anchors.fill: parent
3206 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
3207 && draggedDelegateCount === 0
3208@@ -218,7 +293,25 @@
3209 property int draggedDelegateCount: 0
3210 property int closingIndex: -1
3211
3212+ // FIXME: Workaround Flickable's not keepping its contentX still when resized
3213+ onContentXChanged: { forceItToRemainStillIfBeingResized(); }
3214+ onShiftChanged: { forceItToRemainStillIfBeingResized(); }
3215+ function forceItToRemainStillIfBeingResized() {
3216+ if (root.beingResized && contentX != -shift) {
3217+ contentX = -shift;
3218+ }
3219+ }
3220+
3221 property bool animateX: true
3222+ property bool beingResized: false
3223+ onBeingResizedChanged: {
3224+ if (beingResized) {
3225+ // Brace yourselves for impact!
3226+ selectedIndex = -1;
3227+ phase = 0;
3228+ contentX = -shift;
3229+ }
3230+ }
3231
3232 property bool sideStageDragging: sideStageDragHandle.dragging
3233 property real sideStageDragProgress: sideStageDragHandle.progress
3234@@ -257,7 +350,6 @@
3235 case "overlay":
3236 return 1;
3237 }
3238- print("Unhandled nextInStack case! This shouldn't happen any more when the Dash is an app!");
3239 return -1;
3240 }
3241 property int nextZInStack: indexToZIndex(nextInStack)
3242@@ -293,6 +385,10 @@
3243 }
3244
3245 onShiftedContentXChanged: {
3246+ if (root.beingResized) {
3247+ // Flickabe.contentX wiggles during resizes. Don't react to it.
3248+ return;
3249+ }
3250 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
3251 spreadView.phase = 1;
3252 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
3253@@ -402,8 +498,8 @@
3254 MouseArea {
3255 id: spreadRow
3256 x: spreadView.contentX
3257+ width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
3258 height: root.height
3259- width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
3260
3261 onClicked: {
3262 spreadView.snapTo(0);
3263@@ -412,8 +508,9 @@
3264 Rectangle {
3265 id: sideStageBackground
3266 color: "black"
3267- anchors.fill: parent
3268- anchors.leftMargin: spreadView.width - (1 - sideStageDragHandle.progress) * spreadView.sideStageWidth
3269+ width: spreadView.sideStageWidth * (1 - sideStageDragHandle.progress)
3270+ height: priv.landscapeHeight
3271+ x: spreadView.width - width
3272 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
3273 opacity: spreadView.phase == 0 ? 1 : 0
3274 Behavior on opacity { UbuntuNumberAnimation {} }
3275@@ -421,8 +518,10 @@
3276
3277 Item {
3278 id: sideStageDragHandle
3279- anchors { top: parent.top; bottom: parent.bottom; left: parent.left; leftMargin: spreadView.width - spreadView.sideStageWidth - width }
3280+ anchors.right: sideStageBackground.left
3281+ anchors.top: sideStageBackground.top
3282 width: units.gu(2)
3283+ height: priv.landscapeHeight
3284 z: sideStageBackground.z
3285 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
3286 property real progress: 0
3287@@ -441,7 +540,6 @@
3288
3289 Image {
3290 anchors.centerIn: parent
3291- anchors.horizontalCenterOffset: parent.progress * spreadView.sideStageWidth - (width - parent.width) / 2
3292 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
3293 height: parent.height
3294 source: "graphics/sidestage_handle@20.png"
3295@@ -454,16 +552,19 @@
3296 enabled: spreadView.shiftedContentX == 0
3297 property int startX
3298 property var gesturePoints: new Array()
3299+ property real totalDiff
3300
3301 onPressed: {
3302 gesturePoints = [];
3303 startX = mouseX;
3304+ totalDiff = 0.0;
3305 sideStageDragHandle.progress = 0;
3306 sideStageDragHandle.dragging = true;
3307 }
3308 onMouseXChanged: {
3309+ totalDiff += mouseX - startX;
3310 if (priv.mainStageAppId) {
3311- sideStageDragHandle.progress = Math.max(0, (-startX + mouseX) / spreadView.sideStageWidth);
3312+ sideStageDragHandle.progress = Math.max(0, totalDiff / spreadView.sideStageWidth);
3313 }
3314 gesturePoints.push(mouseX);
3315 }
3316@@ -492,12 +593,26 @@
3317
3318 Repeater {
3319 id: spreadRepeater
3320+ objectName: "spreadRepeater"
3321 model: ApplicationManager
3322
3323 delegate: TransformedTabletSpreadDelegate {
3324 id: spreadTile
3325- height: spreadView.height
3326- width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
3327+ objectName: "spreadDelegate_" + model.appId
3328+ width: {
3329+ if (wantsMainStage) {
3330+ return spreadView.width;
3331+ } else {
3332+ return spreadView.sideStageWidth;
3333+ }
3334+ }
3335+ height: {
3336+ if (wantsMainStage) {
3337+ return spreadView.height;
3338+ } else {
3339+ return priv.landscapeHeight;
3340+ }
3341+ }
3342 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
3343 zIndex: spreadView.indexToZIndex(index)
3344 selected: spreadView.selectedIndex == index
3345@@ -510,6 +625,8 @@
3346 application: ApplicationManager.get(index)
3347 closeable: !isDash
3348
3349+ readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
3350+
3351 readonly property bool isDash: model.appId == "unity8-dash"
3352
3353 // FIXME: A regular binding doesn't update any more after closing an app.
3354@@ -561,12 +678,11 @@
3355 return progress;
3356 }
3357
3358- Binding {
3359- target: spreadTile
3360- property: "orientation"
3361- when: spreadTile.interactive
3362- value: root.orientation
3363- }
3364+ shellOrientationAngle: wantsMainStage ? root.shellOrientationAngle : 0
3365+ shellOrientation: wantsMainStage ? root.shellOrientation : Qt.PortraitOrientation
3366+ shellPrimaryOrientation: wantsMainStage ? root.shellPrimaryOrientation : Qt.PortraitOrientation
3367+ nativeOrientation: wantsMainStage ? root.nativeOrientation : Qt.PortraitOrientation
3368+
3369
3370 onClicked: {
3371 if (spreadView.phase == 2) {
3372@@ -587,6 +703,19 @@
3373 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
3374 }
3375
3376+ Binding {
3377+ target: root
3378+ when: model.appId == priv.mainStageAppId
3379+ property: "mainAppWindowOrientationAngle"
3380+ value: appWindowOrientationAngle
3381+ }
3382+ Binding {
3383+ target: priv
3384+ when: model.appId == priv.mainStageAppId
3385+ property: "mainAppOrientationChangesEnabled"
3386+ value: orientationChangesEnabled
3387+ }
3388+
3389 EasingCurve {
3390 id: snappingCurve
3391 type: EasingCurve.Linear
3392@@ -603,6 +732,7 @@
3393 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
3394 width: root.dragAreaWidth
3395 direction: Direction.Leftwards
3396+ enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
3397
3398 property var gesturePoints: new Array()
3399
3400@@ -631,11 +761,15 @@
3401 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
3402 gesturePoints = [];
3403
3404+ var nextAppInStackIsInMainStage =
3405+ spreadView.nextInStack !== -1 &&
3406+ ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage;
3407+
3408 if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
3409 // If it was a short one-way movement, do the Alt+Tab switch
3410 // no matter if we didn't cross positionMarker1 yet.
3411 spreadView.snapTo(spreadView.nextInStack);
3412- } else if (!dragging) {
3413+ } else {
3414 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
3415 spreadView.snap();
3416 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
3417
3418=== modified file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
3419--- qml/Stages/TransformedTabletSpreadDelegate.qml 2015-02-02 16:23:34 +0000
3420+++ qml/Stages/TransformedTabletSpreadDelegate.qml 2015-04-16 12:11:39 +0000
3421@@ -50,7 +50,9 @@
3422 property int dragOffset: 0
3423
3424 dropShadow: spreadView.active ||
3425- (active && model.stage == ApplicationInfoInterface.MainStage && priv.xTranslate != 0)
3426+ (active
3427+ && (model.stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape)
3428+ && priv.xTranslate != 0)
3429
3430 onSelectedChanged: {
3431 if (selected) {
3432@@ -139,7 +141,8 @@
3433 !snapAnimation.running &&
3434 model.appId !== "unity8-dash" &&
3435 !spreadView.sideStageDragging &&
3436- spreadView.animateX
3437+ spreadView.animateX &&
3438+ !spreadView.beingResized
3439 UbuntuNumberAnimation {
3440 duration: UbuntuAnimation.FastDuration
3441 }
3442
3443=== modified file 'run.sh'
3444--- run.sh 2015-03-11 08:07:31 +0000
3445+++ run.sh 2015-04-16 12:11:39 +0000
3446@@ -44,7 +44,7 @@
3447 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/plugins/LightDM/liblightdm
3448 fi
3449
3450-QML_PHONE_SHELL_ARGS=""
3451+QML_PHONE_SHELL_ARGS="-windowgeometry=40gux68gu -devicename=mako"
3452 if $MOUSE_TOUCH; then
3453 QML_PHONE_SHELL_ARGS="$QML_PHONE_SHELL_ARGS -mousetouch"
3454 fi
3455
3456=== added file 'src/ApplicationArguments.cpp'
3457--- src/ApplicationArguments.cpp 1970-01-01 00:00:00 +0000
3458+++ src/ApplicationArguments.cpp 2015-04-16 12:11:39 +0000
3459@@ -0,0 +1,22 @@
3460+/*
3461+ * Copyright (C) 2015 Canonical, Ltd.
3462+ *
3463+ * This program is free software; you can redistribute it and/or modify
3464+ * it under the terms of the GNU General Public License as published by
3465+ * the Free Software Foundation; version 3.
3466+ *
3467+ * This program is distributed in the hope that it will be useful,
3468+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3469+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3470+ * GNU General Public License for more details.
3471+ *
3472+ * You should have received a copy of the GNU General Public License
3473+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3474+ */
3475+
3476+#include "ApplicationArguments.h"
3477+
3478+ApplicationArguments::ApplicationArguments(QObject *parent)
3479+ : QObject(parent)
3480+{
3481+}
3482
3483=== modified file 'src/ApplicationArguments.h'
3484--- src/ApplicationArguments.h 2014-07-08 19:04:17 +0000
3485+++ src/ApplicationArguments.h 2015-04-16 12:11:39 +0000
3486@@ -1,5 +1,5 @@
3487 /*
3488- * Copyright (C) 2013 Canonical, Ltd.
3489+ * Copyright (C) 2013,2015 Canonical, Ltd.
3490 *
3491 * This program is free software; you can redistribute it and/or modify
3492 * it under the terms of the GNU General Public License as published by
3493@@ -13,7 +13,6 @@
3494 * You should have received a copy of the GNU General Public License
3495 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3496 *
3497- * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
3498 */
3499
3500
3501@@ -22,24 +21,29 @@
3502
3503 #include <QObject>
3504 #include <QSize>
3505-#include <QStringList>
3506+#include <QString>
3507
3508 class ApplicationArguments : public QObject
3509 {
3510 Q_OBJECT
3511+ Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
3512 public:
3513- // Not exposed to the app as setSize isn't invokable
3514+ ApplicationArguments(QObject *parent = nullptr);
3515+
3516+ void setDeviceName(QString deviceName) { m_deviceName = deviceName; }
3517 void setSize(int width, int height) {
3518 m_size.rwidth() = width;
3519 m_size.rheight() = height;
3520 }
3521
3522- Q_INVOKABLE bool hasGeometry() const { return m_size.isValid(); }
3523 Q_INVOKABLE int width() const { return m_size.width(); }
3524 Q_INVOKABLE int height() const { return m_size.height(); }
3525
3526+ QString deviceName() const { return m_deviceName; }
3527+
3528 private:
3529- QSize m_size;
3530+ QSize m_size;
3531+ QString m_deviceName;
3532 };
3533
3534 #endif // APPLICATION_ARGUMENTS_H
3535
3536=== modified file 'src/CMakeLists.txt'
3537--- src/CMakeLists.txt 2014-10-27 09:40:01 +0000
3538+++ src/CMakeLists.txt 2015-04-16 12:11:39 +0000
3539@@ -17,21 +17,21 @@
3540 )
3541
3542 add_executable(${SHELL_APP}
3543- ApplicationArguments.h
3544+ ApplicationArguments.cpp
3545 main.cpp
3546 MouseTouchAdaptor.cpp
3547 CachingNetworkManagerFactory.cpp
3548+ UnityCommandLineParser.cpp
3549 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
3550 )
3551
3552 qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
3553-pkg_check_modules(XCB REQUIRED xcb)
3554-
3555-if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
3556- set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
3557-
3558- target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
3559+
3560+pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
3561+if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
3562+ set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
3563 endif()
3564+target_link_libraries(${SHELL_APP} ${NEEDED_LIBS_LDFLAGS})
3565
3566 target_link_libraries(${SHELL_APP} UbuntuGestures connectivity-qt1)
3567
3568
3569=== modified file 'src/Dash/CMakeLists.txt'
3570--- src/Dash/CMakeLists.txt 2014-10-27 09:40:01 +0000
3571+++ src/Dash/CMakeLists.txt 2015-04-16 12:11:39 +0000
3572@@ -1,6 +1,6 @@
3573 set(DASH_SRCS
3574 main.cpp
3575- ../ApplicationArguments.h
3576+ ../ApplicationArguments.cpp
3577 ../MouseTouchAdaptor.cpp
3578 ../CachingNetworkManagerFactory.cpp
3579 )
3580@@ -9,6 +9,12 @@
3581
3582 qt5_use_modules(unity8-dash Gui Qml Quick Test)
3583
3584+pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
3585+if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
3586+ set_target_properties(unity8-dash PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
3587+endif()
3588+target_link_libraries(unity8-dash ${NEEDED_LIBS_LDFLAGS})
3589+
3590 # For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module.
3591 set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
3592
3593
3594=== added file 'src/UnityCommandLineParser.cpp'
3595--- src/UnityCommandLineParser.cpp 1970-01-01 00:00:00 +0000
3596+++ src/UnityCommandLineParser.cpp 2015-04-16 12:11:39 +0000
3597@@ -0,0 +1,123 @@
3598+/*
3599+ * Copyright (C) 2015 Canonical, Ltd.
3600+ *
3601+ * This program is free software; you can redistribute it and/or modify
3602+ * it under the terms of the GNU General Public License as published by
3603+ * the Free Software Foundation; version 3.
3604+ *
3605+ * This program is distributed in the hope that it will be useful,
3606+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3607+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3608+ * GNU General Public License for more details.
3609+ *
3610+ * You should have received a copy of the GNU General Public License
3611+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3612+ */
3613+
3614+#include "UnityCommandLineParser.h"
3615+
3616+#include <QDebug>
3617+
3618+#define ENV_GRID_UNIT_PX "GRID_UNIT_PX"
3619+#define DEFAULT_GRID_UNIT_PX 8
3620+
3621+UnityCommandLineParser::UnityCommandLineParser(const QCoreApplication &app)
3622+{
3623+ m_gridUnit = getenvFloat(ENV_GRID_UNIT_PX, DEFAULT_GRID_UNIT_PX);
3624+
3625+ QCommandLineParser parser;
3626+ parser.setApplicationDescription("Description: Unity 8 Shell");
3627+ parser.addHelpOption();
3628+
3629+ QCommandLineOption fullscreenOption("fullscreen",
3630+ "Run in fullscreen");
3631+ parser.addOption(fullscreenOption);
3632+
3633+ QCommandLineOption framelessOption("frameless",
3634+ "Run without window borders");
3635+ parser.addOption(framelessOption);
3636+
3637+ QCommandLineOption mousetouchOption("mousetouch",
3638+ "Allow the mouse to provide touch input");
3639+ parser.addOption(mousetouchOption);
3640+
3641+ QCommandLineOption windowGeometryOption(QStringList() << "windowgeometry",
3642+ "Specify the window geometry as [<width>x<height>]", "windowgeometry", "1");
3643+ parser.addOption(windowGeometryOption);
3644+
3645+ QCommandLineOption testabilityOption("testability",
3646+ "DISCOURAGED: Please set QT_LOAD_TESTABILITY instead.\nLoad the testability driver");
3647+ parser.addOption(testabilityOption);
3648+
3649+ QCommandLineOption devicenameOption(QStringList() << "devicename",
3650+ "Specify the device name instead of letting Unity 8 find it out", "devicename", "");
3651+ parser.addOption(devicenameOption);
3652+
3653+ QCommandLineOption modeOption("mode",
3654+ "Whether to run greeter and/or shell [full-greeter, full-shell, greeter, shell]",
3655+ "mode", "full-greeter");
3656+ parser.addOption(modeOption);
3657+
3658+ // Treat args with single dashes the same as arguments with two dashes
3659+ // Ex: -fullscreen == --fullscreen
3660+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
3661+
3662+ parser.process(app);
3663+
3664+ if (parser.isSet(windowGeometryOption))
3665+ {
3666+ QStringList geom = parser.value(windowGeometryOption).split('x');
3667+ if (geom.count() == 2) {
3668+ m_windowGeometry.rwidth() = parsePixelsValue(geom[0]);
3669+ m_windowGeometry.rheight() = parsePixelsValue(geom[1]);
3670+ }
3671+ }
3672+
3673+ m_hasTestability = parser.isSet(testabilityOption);
3674+ m_hasFrameless = parser.isSet(framelessOption);
3675+ m_hasMouseToTouch = parser.isSet(mousetouchOption);
3676+ m_hasFullscreen = parser.isSet(fullscreenOption);
3677+ m_deviceName = parser.value(devicenameOption);
3678+ resolveMode(parser, modeOption);
3679+}
3680+
3681+int UnityCommandLineParser::parsePixelsValue(const QString &str)
3682+{
3683+ if (str.endsWith("gu", Qt::CaseInsensitive)) {
3684+ QString numStr = str;
3685+ numStr.remove(numStr.size() - 2, 2);
3686+ return numStr.toInt() * m_gridUnit;
3687+ } else {
3688+ return str.toInt();
3689+ }
3690+}
3691+
3692+float UnityCommandLineParser::getenvFloat(const char* name, float defaultValue)
3693+{
3694+ QByteArray stringValue = qgetenv(name);
3695+ bool ok;
3696+ float value = stringValue.toFloat(&ok);
3697+ return ok ? value : defaultValue;
3698+}
3699+
3700+void UnityCommandLineParser::resolveMode(QCommandLineParser &parser, QCommandLineOption &modeOption)
3701+{
3702+ // If an invalid option was specified, set it to the default
3703+ // If no default was provided in the QCommandLineOption constructor, abort.
3704+ if (!parser.isSet(modeOption) ||
3705+ (parser.value(modeOption) != "full-greeter" &&
3706+ parser.value(modeOption) != "full-shell" &&
3707+ parser.value(modeOption) != "greeter" &&
3708+ parser.value(modeOption) != "shell")) {
3709+
3710+ if (modeOption.defaultValues().first() != nullptr) {
3711+ m_mode = modeOption.defaultValues().first();
3712+ qWarning() << "Mode argument was not provided or was set to an illegal value."
3713+ " Using default value of --mode=" << m_mode;
3714+ } else {
3715+ qFatal("Shell mode argument was not provided and there is no default mode.");
3716+ }
3717+ } else {
3718+ m_mode = parser.value(modeOption);
3719+ }
3720+}
3721
3722=== added file 'src/UnityCommandLineParser.h'
3723--- src/UnityCommandLineParser.h 1970-01-01 00:00:00 +0000
3724+++ src/UnityCommandLineParser.h 2015-04-16 12:11:39 +0000
3725@@ -0,0 +1,52 @@
3726+/*
3727+ * Copyright (C) 2015 Canonical, Ltd.
3728+ *
3729+ * This program is free software; you can redistribute it and/or modify
3730+ * it under the terms of the GNU General Public License as published by
3731+ * the Free Software Foundation; version 3.
3732+ *
3733+ * This program is distributed in the hope that it will be useful,
3734+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3735+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3736+ * GNU General Public License for more details.
3737+ *
3738+ * You should have received a copy of the GNU General Public License
3739+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3740+ */
3741+
3742+#ifndef UNITY_COMMAND_LINE_PARSER_H
3743+#define UNITY_COMMAND_LINE_PARSER_H
3744+
3745+#include <QCommandLineParser>
3746+#include <QSize>
3747+#include <QString>
3748+
3749+class UnityCommandLineParser {
3750+public:
3751+ UnityCommandLineParser(const QCoreApplication &app);
3752+
3753+ QSize windowGeometry() const { return m_windowGeometry; }
3754+ bool hasTestability() const { return m_hasTestability; }
3755+ bool hasFrameless() const { return m_hasFrameless; }
3756+ bool hasMouseToTouch() const { return m_hasMouseToTouch; }
3757+ bool hasFullscreen() const { return m_hasFullscreen; }
3758+ QString deviceName() const { return m_deviceName; }
3759+ QString mode() const { return m_mode; }
3760+private:
3761+
3762+ int parsePixelsValue(const QString &str);
3763+ static float getenvFloat(const char* name, float defaultValue);
3764+ void resolveMode(QCommandLineParser &parser, QCommandLineOption &modeOption);
3765+
3766+ float m_gridUnit;
3767+
3768+ QSize m_windowGeometry;
3769+ bool m_hasTestability;
3770+ bool m_hasFrameless;
3771+ bool m_hasMouseToTouch;
3772+ bool m_hasFullscreen;
3773+ QString m_deviceName;
3774+ QString m_mode;
3775+};
3776+
3777+#endif // UNITY_COMMAND_LINE_PARSER_H
3778
3779=== modified file 'src/main.cpp'
3780--- src/main.cpp 2015-03-23 23:40:27 +0000
3781+++ src/main.cpp 2015-04-16 12:11:39 +0000
3782@@ -25,11 +25,15 @@
3783 #include <csignal>
3784 #include <libintl.h>
3785
3786+// libandroid-properties
3787+#include <hybris/properties/properties.h>
3788+
3789 // local
3790 #include <paths.h>
3791 #include "MouseTouchAdaptor.h"
3792 #include "ApplicationArguments.h"
3793 #include "CachingNetworkManagerFactory.h"
3794+#include "UnityCommandLineParser.h"
3795
3796 // Ubuntu Gestures
3797 #include <TouchRegistry.h>
3798@@ -45,79 +49,22 @@
3799 QGuiApplication::setApplicationName("unity8");
3800 QGuiApplication *application;
3801
3802- QCommandLineParser parser;
3803- parser.setApplicationDescription("Description: Unity 8 Shell");
3804- parser.addHelpOption();
3805-
3806- QCommandLineOption fullscreenOption("fullscreen",
3807- "Run in fullscreen");
3808- parser.addOption(fullscreenOption);
3809-
3810- QCommandLineOption framelessOption("frameless",
3811- "Run without window borders");
3812- parser.addOption(framelessOption);
3813-
3814- QCommandLineOption mousetouchOption("mousetouch",
3815- "Allow the mouse to provide touch input");
3816- parser.addOption(mousetouchOption);
3817-
3818- QCommandLineOption windowGeometryOption(QStringList() << "windowgeometry",
3819- "Specify the window geometry as [<width>x<height>]", "windowgeometry", "1");
3820- parser.addOption(windowGeometryOption);
3821-
3822- QCommandLineOption testabilityOption("testability",
3823- "DISCOURAGED: Please set QT_LOAD_TESTABILITY instead. \n \
3824-Load the testability driver");
3825- parser.addOption(testabilityOption);
3826-
3827- QCommandLineOption modeOption("mode",
3828- "Whether to run greeter and/or shell [full-greeter, full-shell, greeter, shell]",
3829- "mode", "full-greeter");
3830- parser.addOption(modeOption);
3831-
3832 application = new QGuiApplication(argc, (char**)argv);
3833
3834- // Treat args with single dashes the same as arguments with two dashes
3835- // Ex: -fullscreen == --fullscreen
3836- parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
3837- parser.process(*application);
3838-
3839- QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
3840- if (indicatorProfile.isEmpty()) {
3841- indicatorProfile = "phone";
3842- }
3843+ UnityCommandLineParser parser(*application);
3844
3845 ApplicationArguments qmlArgs;
3846- if (parser.isSet(windowGeometryOption) &&
3847- parser.value(windowGeometryOption).split('x').size() == 2)
3848- {
3849- QStringList geom = parser.value(windowGeometryOption).split('x');
3850- qmlArgs.setSize(geom.at(0).toInt(), geom.at(1).toInt());
3851- }
3852-
3853- // If an invalid option was specified, set it to the default
3854- // If no default was provided in the QCommandLineOption constructor, abort.
3855- QString shellMode;
3856- if(!parser.isSet(modeOption) ||
3857- (parser.value(modeOption) != "full-greeter" &&
3858- parser.value(modeOption) != "full-shell" &&
3859- parser.value(modeOption) != "greeter" &&
3860- parser.value(modeOption) != "shell"))
3861- {
3862- if (modeOption.defaultValues().first() != nullptr) {
3863- shellMode = modeOption.defaultValues().first();
3864- qWarning() << "Mode argument was not provided or was set to an illegal value. Using default value of --mode=" << shellMode;
3865- } else {
3866- qFatal("Shell mode argument was not provided and there is no default mode,");
3867- }
3868-
3869+ if (!parser.deviceName().isEmpty()) {
3870+ qmlArgs.setDeviceName(parser.deviceName());
3871 } else {
3872- shellMode = parser.value(modeOption);
3873+ char buffer[200];
3874+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
3875+ qmlArgs.setDeviceName(QString(buffer));
3876 }
3877
3878 // The testability driver is only loaded by QApplication but not by QGuiApplication.
3879 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
3880- if (parser.isSet(testabilityOption) || getenv("QT_LOAD_TESTABILITY")) {
3881+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
3882 QLibrary testLib(QLatin1String("qttestability"));
3883 if (testLib.load()) {
3884 typedef void (*TasInitialize)(void);
3885@@ -141,22 +88,25 @@
3886 view->setTitle("Unity8 Shell");
3887 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
3888 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
3889- view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);
3890- view->rootContext()->setContextProperty("shellMode", shellMode);
3891- if (parser.isSet(framelessOption)) {
3892+ view->rootContext()->setContextProperty("shellMode", parser.mode());
3893+ if (parser.hasFrameless()) {
3894 view->setFlags(Qt::FramelessWindowHint);
3895 }
3896 TouchRegistry touchRegistry;
3897 view->installEventFilter(&touchRegistry);
3898+ if (parser.windowGeometry().isValid()) {
3899+ view->setWidth(parser.windowGeometry().width());
3900+ view->setHeight(parser.windowGeometry().height());
3901+ }
3902
3903 // You will need this if you want to interact with touch-only components using a mouse
3904 // Needed only when manually testing on a desktop.
3905 MouseTouchAdaptor *mouseTouchAdaptor = 0;
3906- if (parser.isSet(mousetouchOption)) {
3907+ if (parser.hasMouseToTouch()) {
3908 mouseTouchAdaptor = MouseTouchAdaptor::instance();
3909 }
3910
3911- QUrl source(::qmlDirectory()+"Shell.qml");
3912+ QUrl source(::qmlDirectory()+"OrientedShell.qml");
3913 prependImportPaths(view->engine(), ::overrideImportPaths());
3914 if (!isMirServer) {
3915 prependImportPaths(view->engine(), ::nonMirImportPaths());
3916@@ -169,7 +119,7 @@
3917 view->setSource(source);
3918 QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
3919
3920- if (isMirServer || parser.isSet(fullscreenOption)) {
3921+ if (isMirServer || parser.hasFullscreen()) {
3922 view->showFullScreen();
3923 } else {
3924 view->show();
3925
3926=== modified file 'tests/autopilot/unity8/fixture_setup.py'
3927--- tests/autopilot/unity8/fixture_setup.py 2015-04-07 11:48:34 +0000
3928+++ tests/autopilot/unity8/fixture_setup.py 2015-04-16 12:11:39 +0000
3929@@ -17,18 +17,146 @@
3930 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3931 #
3932
3933+import os
3934+import subprocess
3935+import threading
3936 import fixtures
3937-import subprocess
3938 import logging
3939
3940 from autopilot import introspection
3941+from autopilot.matchers import Eventually
3942+from testtools.matchers import Equals
3943+from ubuntuuitoolkit import fixture_setup
3944
3945 from unity8 import (
3946 get_binary_path,
3947+ sensors,
3948 process_helpers
3949 )
3950+
3951 from unity8.shell import emulators
3952
3953+from unity8.shell.emulators import (
3954+ main_window as main_window_emulator
3955+)
3956+
3957+from unity8 import (
3958+ get_lib_path,
3959+ get_binary_path,
3960+ get_mocks_library_path,
3961+ get_default_extra_mock_libraries,
3962+ get_data_dirs
3963+)
3964+
3965+logger = logging.getLogger(__name__)
3966+
3967+class LaunchUnityWithFakeSensors(fixtures.Fixture):
3968+
3969+ """Fixture to launch Unity8 with an injectable sensors backend.
3970+
3971+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
3972+
3973+ """
3974+
3975+ unity_proxy = None
3976+ main_win = None
3977+
3978+ def setUp(self):
3979+ """Restart Unity8 with testability and create sensors."""
3980+ super(LaunchUnityWithFakeSensors, self).setUp()
3981+ self.useFixture(
3982+ fixture_setup.InitctlEnvironmentVariable(
3983+ UBUNTU_PLATFORM_API_TEST_OVERRIDE='sensors'))
3984+
3985+ self.addCleanup(process_helpers.stop_job, 'unity8')
3986+ restart_thread = threading.Thread(
3987+ target=self._restart_unity_with_testability)
3988+ restart_thread.start()
3989+
3990+ self._create_sensors()
3991+
3992+ restart_thread.join()
3993+ self.fake_sensors = sensors.FakePlatformSensors()
3994+
3995+ def _get_lightdm_mock_path(self):
3996+ lib_path = get_mocks_library_path()
3997+ lightdm_mock_path = os.path.abspath(
3998+ os.path.join(lib_path, "LightDM", "liblightdm")
3999+ )
4000+
4001+ if not os.path.exists(lightdm_mock_path):
4002+ raise RuntimeError(
4003+ "LightDM mock does not exist at path '%s'."
4004+ % (lightdm_mock_path)
4005+ )
4006+ return lightdm_mock_path
4007+
4008+ def _get_qml_import_path_with_mock(self):
4009+ """Return the QML2_IMPORT_PATH value with the mock path prepended."""
4010+ qml_import_path = [get_mocks_library_path()]
4011+ if os.getenv('QML2_IMPORT_PATH') is not None:
4012+ qml_import_path.append(os.getenv('QML2_IMPORT_PATH'))
4013+
4014+ qml_import_path = ':'.join(qml_import_path)
4015+ return qml_import_path
4016+
4017+ def _restart_unity_with_testability(self):
4018+ _environment = {}
4019+
4020+ data_dirs = get_data_dirs(True)
4021+ if data_dirs is not None:
4022+ _environment['XDG_DATA_DIRS'] = data_dirs
4023+
4024+ _environment['QML2_IMPORT_PATH'] = (
4025+ self._get_qml_import_path_with_mock()
4026+ )
4027+
4028+ new_ld_library_path = [
4029+ get_default_extra_mock_libraries(),
4030+ self._get_lightdm_mock_path()
4031+ ]
4032+ if os.getenv('LD_LIBRARY_PATH') is not None:
4033+ new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
4034+ new_ld_library_path = ':'.join(new_ld_library_path)
4035+ _environment['LD_LIBRARY_PATH'] = new_ld_library_path
4036+
4037+ # FIXME: we shouldn't be doing this
4038+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
4039+ # /tmp/mir_socket as last resort
4040+ try:
4041+ os.unlink(
4042+ os.getenv('MIR_SOCKET',
4043+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
4044+ "mir_socket")))
4045+ except OSError:
4046+ pass
4047+ try:
4048+ os.unlink("/tmp/mir_socket")
4049+ except OSError:
4050+ pass
4051+
4052+ binary_arg = "BINARY=%s" % get_binary_path()
4053+ env_args = ["%s=%s" % (k, v) for k, v in _environment.items()]
4054+ args = [binary_arg] + env_args
4055+ self.unity_proxy = process_helpers.restart_unity_with_testability(*args)
4056+ self.main_win = self.unity_proxy.select_single(main_window_emulator.QQuickView)
4057+
4058+ def _create_sensors(self):
4059+ # Wait for unity to start running.
4060+ Eventually(Equals(True)).match(
4061+ lambda: process_helpers.is_job_running('unity8'))
4062+
4063+ # Wait for the sensors fifo file to be created.
4064+ fifo_path = '/tmp/sensor-fifo-{0}'.format(
4065+ process_helpers.get_unity_pid())
4066+ Eventually(Equals(True)).match(
4067+ lambda: os.path.exists(fifo_path))
4068+
4069+ with open(fifo_path, 'w') as fifo:
4070+ fifo.write('create accel 0 1000 0.1\n')
4071+ fifo.write('create light 0 10 1\n')
4072+ fifo.write('create proximity\n')
4073+
4074 logger = logging.getLogger(__name__)
4075
4076 class LaunchDashApp(fixtures.Fixture):
4077
4078=== modified file 'tests/autopilot/unity8/indicators/tests/test_display_indicator.py'
4079--- tests/autopilot/unity8/indicators/tests/test_display_indicator.py 2015-03-20 11:31:40 +0000
4080+++ tests/autopilot/unity8/indicators/tests/test_display_indicator.py 2015-04-16 12:11:39 +0000
4081@@ -16,9 +16,12 @@
4082 # You should have received a copy of the GNU General Public License
4083 # along with this program. If not, see <http://www.gnu.org/licenses/>.
4084
4085+from autopilot import platform
4086+
4087 from unity8 import (
4088 fixture_setup,
4089 indicators,
4090+ process_helpers
4091 )
4092 from unity8.indicators import tests
4093
4094@@ -39,6 +42,9 @@
4095
4096 self.assertTrue(display_indicator.is_indicator_icon_visible())
4097
4098+ self.fake_sensors.set_orientation_top_down()
4099+ # TODO how to get the shell orientation?
4100+
4101 def test_indicator_icon_must_not_be_visible_after_rotation_unlocked(self):
4102 rotation_locked = fixture_setup.DisplayRotationLock(True)
4103 self.useFixture(rotation_locked)
4104
4105=== modified file 'tests/autopilot/unity8/process_helpers.py'
4106--- tests/autopilot/unity8/process_helpers.py 2015-04-01 14:50:19 +0000
4107+++ tests/autopilot/unity8/process_helpers.py 2015-04-16 12:11:39 +0000
4108@@ -57,7 +57,7 @@
4109 """
4110 if unity_proxy_obj is None:
4111 try:
4112- pid = _get_unity_pid()
4113+ pid = get_unity_pid()
4114 unity = _get_unity_proxy_object(pid)
4115 main_window = unity.select_single(main_window_emulator.QQuickView)
4116 except ProcessSearchError as e:
4117@@ -261,7 +261,7 @@
4118 raise CannotAccessUnity(str(error))
4119
4120
4121-def _get_unity_pid():
4122+def get_unity_pid():
4123 try:
4124 return get_job_pid('unity8')
4125 except JobError as error:
4126
4127=== added file 'tests/autopilot/unity8/sensors.py'
4128--- tests/autopilot/unity8/sensors.py 1970-01-01 00:00:00 +0000
4129+++ tests/autopilot/unity8/sensors.py 2015-04-16 12:11:39 +0000
4130@@ -0,0 +1,90 @@
4131+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4132+#
4133+# Unity Autopilot Test Suite
4134+# Copyright (C) 2015 Canonical
4135+#
4136+# This program is free software: you can redistribute it and/or modify
4137+# it under the terms of the GNU General Public License as published by
4138+# the Free Software Foundation, either version 3 of the License, or
4139+# (at your option) any later version.
4140+#
4141+# This program is distributed in the hope that it will be useful,
4142+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4143+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4144+# GNU General Public License for more details.
4145+#
4146+# You should have received a copy of the GNU General Public License
4147+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4148+#
4149+
4150+"""Control module for event injection to the fake/test platform sensors."""
4151+
4152+from unity8 import process_helpers
4153+
4154+
4155+class FakePlatformSensors:
4156+
4157+ def __init__(self, pid=None):
4158+ self.pid = pid or process_helpers.get_unity_pid()
4159+
4160+ def set_orientation_top_up(self):
4161+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
4162+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
4163+ fifo.write("70 accel -9.797073 -0.555455 1.019930\n")
4164+ fifo.write("70 accel -10.141838 -0.770933 0.632069\n")
4165+ fifo.write("70 accel -12.057199 -1.259350 1.690306\n")
4166+ fifo.write("70 accel -19.282900 -3.926491 3.098097\n")
4167+ fifo.write("70 accel -14.480132 -14.269443 1.216254\n")
4168+ fifo.write("70 accel 16.419436 4.242526 -7.714118\n")
4169+ fifo.write("70 accel 5.583278 8.279149 -1.848324\n")
4170+ fifo.write("70 accel 1.422156 8.547300 0.416591\n")
4171+ fifo.write("70 accel 4.357447 9.988609 -0.110133\n")
4172+ fifo.write("70 accel 0.699107 9.840169 0.756568\n")
4173+ fifo.write("70 accel 1.364695 9.844957 -0.287304\n")
4174+ fifo.flush()
4175+
4176+ def set_orientation_top_down(self):
4177+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
4178+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
4179+ fifo.write("70 accel 9.538500 -0.603339 1.292869\n")
4180+ fifo.write("70 accel 9.485827 -0.636858 1.422156\n")
4181+ fifo.write("70 accel 9.677363 -0.402226 1.374272\n")
4182+ fifo.write("70 accel 9.303867 -0.507571 1.283292\n")
4183+ fifo.write("70 accel 8.604761 -1.015141 1.436521\n")
4184+ fifo.write("70 accel 7.580042 -2.001553 0.521936\n")
4185+ fifo.write("70 accel 7.503428 -4.247314 0.502782\n")
4186+ fifo.write("70 accel 7.067683 -7.240066 0.842759\n")
4187+ fifo.write("70 accel 6.488286 -9.873688 -0.541090\n")
4188+ fifo.write("70 accel 6.229713 -9.241618 -1.048660\n")
4189+ fifo.write("70 accel 4.046201 -9.198523 -0.057461\n")
4190+ fifo.write("70 accel 2.398990 -9.629479 0.957681\n")
4191+ fifo.write("70 accel 1.632846 -9.361329 -0.311246\n")
4192+ fifo.write("70 accel -0.181959 -9.696517 -0.301669\n")
4193+ fifo.flush()
4194+
4195+ def set_orientation_left_up(self):
4196+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
4197+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
4198+ fifo.write("70 accel 0.196325 9.878476 0.948104\n")
4199+ fifo.write("70 accel 0.258574 9.955091 1.091756\n")
4200+ fifo.write("70 accel 0.287304 10.041282 1.134852\n")
4201+ fifo.write("70 accel 1.537078 10.553641 1.561020\n")
4202+ fifo.write("70 accel 8.130709 10.093954 2.561796\n")
4203+ fifo.write("70 accel -0.229843 5.348647 1.723825\n")
4204+ fifo.write("70 accel -9.916783 0.488417 -3.418920\n")
4205+ fifo.write("70 accel -13.417107 -0.416591 -2.360683\n")
4206+ fifo.write("70 accel -13.872005 -2.049437 -0.574608\n")
4207+ fifo.flush()
4208+
4209+ def set_orientation_right_up(self):
4210+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
4211+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
4212+ fifo.write("70 accel -0.799663 9.988609 1.197101\n")
4213+ fifo.write("70 accel -0.861913 9.864111 0.866701\n")
4214+ fifo.write("70 accel -1.776498 9.830592 1.273715\n")
4215+ fifo.write("70 accel -13.158532 2.217031 1.091756\n")
4216+ fifo.write("70 accel 5.056554 1.067814 0.799663\n")
4217+ fifo.write("70 accel 14.882358 2.896984 1.221043\n")
4218+ fifo.write("70 accel 9.466674 -0.363919 -1.029507\n")
4219+ fifo.write("70 accel 12.253524 -0.186748 -0.311246\n")
4220+ fifo.flush()
4221
4222=== modified file 'tests/autopilot/unity8/shell/emulators/main_window.py'
4223--- tests/autopilot/unity8/shell/emulators/main_window.py 2015-02-24 10:15:33 +0000
4224+++ tests/autopilot/unity8/shell/emulators/main_window.py 2015-04-16 12:11:39 +0000
4225@@ -169,6 +169,18 @@
4226 objectName='pinPadButton{}'.format(button_id)
4227 )
4228
4229+ def get_shell_orientation_angle(self):
4230+ return self._get_shell().orientationAngle
4231+
4232+ def get_shell_orientation(self):
4233+ return self._get_shell().orientation
4234+
4235+ def get_shell_primary_orientation(self):
4236+ return self._get_shell().primaryOrientation
4237+
4238+ def get_shell_native_orientation(self):
4239+ return self._get_shell().nativeOrientation
4240+
4241 @autopilot_logging.log_action(logger.info)
4242 def wait_for_notification(self):
4243 """Wait for a notification dialog to appear.
4244
4245=== modified file 'tests/autopilot/unity8/shell/tests/__init__.py'
4246--- tests/autopilot/unity8/shell/tests/__init__.py 2015-04-09 14:00:54 +0000
4247+++ tests/autopilot/unity8/shell/tests/__init__.py 2015-04-16 12:11:39 +0000
4248@@ -378,6 +378,8 @@
4249
4250 """
4251 self._proxy = proxy
4252+ print("main_window/proxy - # of children: ", len(proxy.get_children()))
4253+ print("main_window/proxy - properties: ", proxy.get_properties())
4254 self.addCleanup(self._clear_proxy)
4255
4256 def _clear_proxy(self):
4257
4258=== added file 'tests/autopilot/unity8/shell/tests/test_rotation.py'
4259--- tests/autopilot/unity8/shell/tests/test_rotation.py 1970-01-01 00:00:00 +0000
4260+++ tests/autopilot/unity8/shell/tests/test_rotation.py 2015-04-16 12:11:39 +0000
4261@@ -0,0 +1,134 @@
4262+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4263+#
4264+# Unity Autopilot Test Suite
4265+# Copyright (C) 2015 Canonical
4266+#
4267+# This program is free software: you can redistribute it and/or modify
4268+# it under the terms of the GNU General Public License as published by
4269+# the Free Software Foundation, either version 3 of the License, or
4270+# (at your option) any later version.
4271+#
4272+# This program is distributed in the hope that it will be useful,
4273+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4274+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4275+# GNU General Public License for more details.
4276+#
4277+# You should have received a copy of the GNU General Public License
4278+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4279+#
4280+
4281+"""Tests for shell-rotation"""
4282+
4283+from autopilot.platform import model
4284+from unity8 import (
4285+ shell,
4286+ fixture_setup,
4287+ indicators,
4288+ process_helpers
4289+)
4290+import os
4291+from unity8.shell import tests
4292+import ubuntuuitoolkit
4293+import logging
4294+from testtools.matchers import Equals, NotEquals
4295+from autopilot.matchers import Eventually
4296+
4297+logger = logging.getLogger(__name__)
4298+
4299+class RotationBase(tests.UnityTestCase):
4300+ """Base class for all shell-rotation tests that provides helper methods."""
4301+
4302+ scenarios = tests._get_device_emulation_scenarios()
4303+
4304+ def setUp(self):
4305+ if model() == 'Desktop':
4306+ self.skipTest('Test cannot be run on the desktop.')
4307+ super(RotationBase, self).setUp()
4308+ self._qml_mock_enabled = False
4309+ self._data_dirs_mock_enabled = False
4310+
4311+ def _assert_change_of_orientation_and_angle(self):
4312+ tmp_o = self.shell_proxy.orientation
4313+ tmp_a = self.shell_proxy.orientationAngle
4314+ self.assertThat(self.orientation, Equals(tmp_o))
4315+ self.assertThat(self.angle, Equals(tmp_a))
4316+
4317+ def test_fake_sensor(self):
4318+ unity_with_sensors = fixture_setup.LaunchUnityWithFakeSensors()
4319+ self.useFixture(unity_with_sensors)
4320+ process_helpers.unlock_unity(unity_with_sensors.unity_proxy)
4321+ fake_sensors = unity_with_sensors.fake_sensors
4322+ oriented_shell_proxy = unity_with_sensors.main_win.select_single('OrientedShell')
4323+
4324+ fake_sensors.set_orientation_top_up()
4325+ target_orientation = 1
4326+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(target_orientation), timeout=15))
4327+
4328+ fake_sensors.set_orientation_right_up()
4329+ target_orientation = 8
4330+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(target_orientation), timeout=15))
4331+
4332+ fake_sensors.set_orientation_top_down()
4333+ target_orientation = 4
4334+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(target_orientation), timeout=15))
4335+
4336+ fake_sensors.set_orientation_left_up()
4337+ target_orientation = 2
4338+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(target_orientation), timeout=15))
4339+
4340+ def test_rotation_with_webbrowser_app(self):
4341+ """Do an orientation-change and verify that an app and the shell adapted correctly"""
4342+
4343+ unity_with_sensors = fixture_setup.LaunchUnityWithFakeSensors()
4344+ self.useFixture(unity_with_sensors)
4345+ process_helpers.unlock_unity(unity_with_sensors.unity_proxy)
4346+ fake_sensors = unity_with_sensors.fake_sensors
4347+ oriented_shell_proxy = unity_with_sensors.main_win.select_single('OrientedShell')
4348+ self.shell_proxy = unity_with_sensors.main_win.select_single('Shell')
4349+
4350+ # launch an application
4351+ self.launch_upstart_application('webbrowser-app')
4352+ unity_with_sensors.main_win.show_dash_from_launcher()
4353+ unity_with_sensors.main_win.launch_application('webbrowser-app')
4354+ self.assertThat(unity_with_sensors.main_win.get_current_focused_app_id(), Eventually(Equals('webbrowser-app')))
4355+
4356+ # get default orientation and angle
4357+ self.orientation = self.shell_proxy.orientation
4358+ self.angle = self.shell_proxy.orientationAngle
4359+
4360+ # check if fake sensors affect orientation and angle
4361+ fake_sensors.set_orientation_top_up()
4362+ self.orientation = 1
4363+ self.angle = 0
4364+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(self.orientation), timeout=15))
4365+ if (self.orientation & oriented_shell_proxy.supportedOrientations):
4366+ self._assert_change_of_orientation_and_angle()
4367+ else:
4368+ logger.info("unsupported orientation TOP-UP. skipped.")
4369+
4370+ fake_sensors.set_orientation_right_up()
4371+ self.orientation = 8
4372+ self.angle = 90
4373+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(self.orientation), timeout=15))
4374+ if (self.orientation & oriented_shell_proxy.supportedOrientations):
4375+ self._assert_change_of_orientation_and_angle()
4376+ else:
4377+ logger.info("unsupported orientation RIGHT-UP. skipped.")
4378+
4379+ fake_sensors.set_orientation_top_down()
4380+ self.orientation = 4
4381+ self.angle = 180
4382+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(self.orientation), timeout=15))
4383+ if (self.orientation & oriented_shell_proxy.supportedOrientations):
4384+ self._assert_change_of_orientation_and_angle()
4385+ else:
4386+ logger.info("unsupported orientation TOP-DOWN. skipped.")
4387+
4388+ fake_sensors.set_orientation_left_up()
4389+ self.orientation = 2
4390+ self.angle = 270
4391+ self.assertThat(oriented_shell_proxy.physicalOrientation, Eventually(Equals(self.orientation), timeout=15))
4392+ if (self.orientation & oriented_shell_proxy.supportedOrientations):
4393+ self._assert_change_of_orientation_and_angle()
4394+ else:
4395+ logger.info("unsupported orientation LEFT-UP. skipped.")
4396
4397=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
4398--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-08-28 23:16:07 +0000
4399+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-04-16 12:11:39 +0000
4400@@ -1,5 +1,5 @@
4401 /*
4402- * Copyright (C) 2013-2014 Canonical, Ltd.
4403+ * Copyright (C) 2013-2015 Canonical, Ltd.
4404 *
4405 * This program is free software; you can redistribute it and/or modify
4406 * it under the terms of the GNU General Public License as published by
4407@@ -34,6 +34,11 @@
4408 , m_focused(false)
4409 , m_fullscreen(false)
4410 , m_session(0)
4411+ , m_supportedOrientations(Qt::PortraitOrientation |
4412+ Qt::LandscapeOrientation |
4413+ Qt::InvertedPortraitOrientation |
4414+ Qt::InvertedLandscapeOrientation)
4415+ , m_rotatesWindowContents(false)
4416 , m_manualSurfaceCreation(false)
4417 {
4418 }
4419@@ -45,6 +50,11 @@
4420 , m_focused(false)
4421 , m_fullscreen(false)
4422 , m_session(0)
4423+ , m_supportedOrientations(Qt::PortraitOrientation |
4424+ Qt::LandscapeOrientation |
4425+ Qt::InvertedPortraitOrientation |
4426+ Qt::InvertedLandscapeOrientation)
4427+ , m_rotatesWindowContents(false)
4428 , m_manualSurfaceCreation(false)
4429 {
4430 }
4431@@ -99,9 +109,17 @@
4432
4433 void ApplicationInfo::setScreenshotId(const QString &screenshotId)
4434 {
4435- QString screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
4436- .arg(qmlDirectory())
4437- .arg(screenshotId);
4438+ QString screenshotFileName;
4439+
4440+ if (screenshotId.endsWith(".svg")) {
4441+ screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2")
4442+ .arg(qmlDirectory())
4443+ .arg(screenshotId);
4444+ } else {
4445+ screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
4446+ .arg(qmlDirectory())
4447+ .arg(screenshotId);
4448+ }
4449
4450 if (screenshotFileName != m_screenshotFileName) {
4451 m_screenshotFileName = screenshotFileName;
4452@@ -145,6 +163,9 @@
4453
4454 if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
4455 QTimer::singleShot(500, this, SLOT(createSession()));
4456+ } else if (m_state == ApplicationInfo::Stopped) {
4457+ delete m_session;
4458+ m_session = nullptr;
4459 }
4460 }
4461 }
4462@@ -172,3 +193,23 @@
4463 Q_EMIT manualSurfaceCreationChanged(value);
4464 }
4465 }
4466+
4467+Qt::ScreenOrientations ApplicationInfo::supportedOrientations() const
4468+{
4469+ return m_supportedOrientations;
4470+}
4471+
4472+void ApplicationInfo::setSupportedOrientations(Qt::ScreenOrientations orientations)
4473+{
4474+ m_supportedOrientations = orientations;
4475+}
4476+
4477+bool ApplicationInfo::rotatesWindowContents() const
4478+{
4479+ return m_rotatesWindowContents;
4480+}
4481+
4482+void ApplicationInfo::setRotatesWindowContents(bool value)
4483+{
4484+ m_rotatesWindowContents = value;
4485+}
4486
4487=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
4488--- tests/mocks/Unity/Application/ApplicationInfo.h 2014-09-15 19:05:32 +0000
4489+++ tests/mocks/Unity/Application/ApplicationInfo.h 2015-04-16 12:11:39 +0000
4490@@ -1,5 +1,5 @@
4491 /*
4492- * Copyright (C) 2013-2014 Canonical, Ltd.
4493+ * Copyright (C) 2013-2015 Canonical, Ltd.
4494 *
4495 * This program is free software; you can redistribute it and/or modify
4496 * it under the terms of the GNU General Public License as published by
4497@@ -80,6 +80,12 @@
4498 void setFullscreen(bool value);
4499 bool fullscreen() const { return m_fullscreen; }
4500
4501+ Qt::ScreenOrientations supportedOrientations() const override;
4502+ void setSupportedOrientations(Qt::ScreenOrientations orientations);
4503+
4504+ bool rotatesWindowContents() const override;
4505+ void setRotatesWindowContents(bool value);
4506+
4507 bool manualSurfaceCreation() const { return m_manualSurfaceCreation; }
4508 void setManualSurfaceCreation(bool value);
4509
4510@@ -108,6 +114,8 @@
4511 bool m_focused;
4512 bool m_fullscreen;
4513 Session* m_session;
4514+ Qt::ScreenOrientations m_supportedOrientations;
4515+ bool m_rotatesWindowContents;
4516
4517 bool m_manualSurfaceCreation;
4518 };
4519
4520=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
4521--- tests/mocks/Unity/Application/ApplicationManager.cpp 2015-03-10 14:25:12 +0000
4522+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2015-04-16 12:11:39 +0000
4523@@ -1,5 +1,5 @@
4524 /*
4525- * Copyright (C) 2013-2014 Canonical, Ltd.
4526+ * Copyright (C) 2013-2015 Canonical, Ltd.
4527 *
4528 * This program is free software; you can redistribute it and/or modify
4529 * it under the terms of the GNU General Public License as published by
4530@@ -348,6 +348,13 @@
4531
4532 void ApplicationManager::buildListOfAvailableApplications()
4533 {
4534+ /*
4535+ ATTENTION!
4536+ Be careful when changing application properties here as some qmltests
4537+ rely on them being the way it's specified here (e.g. that camera-app
4538+ is fullscreen, that twitter-webapp can rotate in all directions, etc)
4539+ */
4540+
4541 ApplicationInfo *application;
4542
4543 application = new ApplicationInfo(this);
4544@@ -355,6 +362,7 @@
4545 application->setName("Unity 8 Mock Dash");
4546 application->setScreenshotId("unity8-dash");
4547 application->setStage(ApplicationInfo::MainStage);
4548+ application->setSupportedOrientations(Qt::PrimaryOrientation);
4549 m_availableApplications.append(application);
4550
4551 application = new ApplicationInfo(this);
4552@@ -363,6 +371,8 @@
4553 application->setScreenshotId("dialer");
4554 application->setIconId("dialer-app");
4555 application->setStage(ApplicationInfo::SideStage);
4556+ application->setSupportedOrientations(Qt::PortraitOrientation
4557+ | Qt::InvertedPortraitOrientation);
4558 m_availableApplications.append(application);
4559
4560 application = new ApplicationInfo(this);
4561@@ -371,6 +381,11 @@
4562 application->setScreenshotId("camera");
4563 application->setIconId("camera");
4564 application->setFullscreen(true);
4565+ application->setSupportedOrientations(Qt::PortraitOrientation
4566+ | Qt::LandscapeOrientation
4567+ | Qt::InvertedPortraitOrientation
4568+ | Qt::InvertedLandscapeOrientation);
4569+ application->setRotatesWindowContents(true);
4570 m_availableApplications.append(application);
4571
4572 application = new ApplicationInfo(this);
4573@@ -416,13 +431,35 @@
4574 application->setAppId("gmail-webapp");
4575 application->setName("GMail");
4576 application->setIconId("gmail");
4577+ application->setScreenshotId("gmail-webapp.svg");
4578+ application->setFullscreen(false);
4579+ application->setStage(ApplicationInfo::MainStage);
4580+ application->setSupportedOrientations(Qt::PortraitOrientation
4581+ | Qt::LandscapeOrientation
4582+ | Qt::InvertedPortraitOrientation
4583+ | Qt::InvertedLandscapeOrientation);
4584+ m_availableApplications.append(application);
4585+
4586+ application = new ApplicationInfo(this);
4587+ application->setAppId("music-app");
4588+ application->setName("Music");
4589+ application->setIconId("soundcloud");
4590+ application->setScreenshotId("music");
4591+ application->setFullscreen(false);
4592+ application->setStage(ApplicationInfo::MainStage);
4593+ application->setSupportedOrientations(Qt::PortraitOrientation
4594+ | Qt::LandscapeOrientation
4595+ | Qt::InvertedPortraitOrientation
4596+ | Qt::InvertedLandscapeOrientation);
4597 m_availableApplications.append(application);
4598
4599 application = new ApplicationInfo(this);
4600 application->setAppId("ubuntu-weather-app");
4601 application->setName("Weather");
4602 application->setIconId("weather");
4603- application->setStage(ApplicationInfo::SideStage);
4604+ application->setScreenshotId("ubuntu-weather-app.svg");
4605+ application->setSupportedOrientations(Qt::LandscapeOrientation
4606+ | Qt::InvertedLandscapeOrientation);
4607 m_availableApplications.append(application);
4608
4609 application = new ApplicationInfo(this);
4610
4611=== modified file 'tests/mocks/Unity/Application/CMakeLists.txt'
4612--- tests/mocks/Unity/Application/CMakeLists.txt 2014-10-06 15:45:25 +0000
4613+++ tests/mocks/Unity/Application/CMakeLists.txt 2015-04-16 12:11:39 +0000
4614@@ -1,4 +1,6 @@
4615-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4616+include_directories(
4617+ ${APPLICATION_API_INCLUDE_DIRS}
4618+)
4619
4620 set(FakeUnityApplicationQml_SOURCES
4621 plugin.cpp
4622@@ -14,6 +16,7 @@
4623 SurfaceManager.cpp
4624 SessionModel.h
4625 UbuntuKeyboardInfo.cpp
4626+ VirtualKeyboard.cpp
4627 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
4628 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
4629 )
4630
4631=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp'
4632--- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2015-03-12 14:45:44 +0000
4633+++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2015-04-16 12:11:39 +0000
4634@@ -1,5 +1,5 @@
4635 /*
4636- * Copyright (C) 2014 Canonical, Ltd.
4637+ * Copyright (C) 2014-2015 Canonical, Ltd.
4638 *
4639 * This program is free software; you can redistribute it and/or modify
4640 * it under the terms of the GNU General Public License as published by
4641@@ -39,14 +39,12 @@
4642 , m_type(type)
4643 , m_state(state)
4644 , m_live(true)
4645- , m_orientation(Qt::PortraitOrientation)
4646+ , m_orientationAngle(Angle0)
4647+ , m_qmlItem(nullptr)
4648+ , m_screenshotUrl(screenshot)
4649 , m_touchPressCount(0)
4650 , m_touchReleaseCount(0)
4651- , m_qmlItem(nullptr)
4652- , m_screenshotUrl(screenshot)
4653 {
4654- qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
4655-
4656 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
4657 Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
4658 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
4659@@ -55,9 +53,6 @@
4660
4661 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
4662
4663- connect(this, &QQuickItem::focusChanged,
4664- this, &MirSurfaceItem::onFocusChanged);
4665-
4666 // The assumptions I make here really should hold.
4667 QQuickView *quickView =
4668 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
4669@@ -117,17 +112,17 @@
4670 }
4671 }
4672
4673-void MirSurfaceItem::setOrientation(const Qt::ScreenOrientation orientation)
4674+void MirSurfaceItem::setOrientationAngle(OrientationAngle angle)
4675 {
4676- if (m_orientation == orientation)
4677+ if (m_orientationAngle == angle)
4678 return;
4679
4680- m_orientation = orientation;
4681-
4682- QQmlProperty orientationProp(m_qmlItem, "orientation");
4683- orientationProp.write(QVariant::fromValue(orientation));
4684-
4685- Q_EMIT orientationChanged();
4686+ m_orientationAngle = angle;
4687+
4688+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
4689+ orientationProp.write(QVariant::fromValue(m_orientationAngle));
4690+
4691+ Q_EMIT orientationAngleChanged(m_orientationAngle);
4692 }
4693
4694 void MirSurfaceItem::setSession(Session* session)
4695@@ -152,15 +147,6 @@
4696 }
4697 }
4698
4699-void MirSurfaceItem::onFocusChanged()
4700-{
4701- if (!hasFocus()) {
4702- // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself.
4703- // Didn't have time to debug yet.
4704- //Q_EMIT inputMethodDismissed();
4705- }
4706-}
4707-
4708 void MirSurfaceItem::setState(MirSurfaceItem::State newState)
4709 {
4710 if (newState != m_state) {
4711@@ -197,9 +183,6 @@
4712 if (event->touchPointStates() & Qt::TouchPointPressed) {
4713 ++m_touchPressCount;
4714 Q_EMIT touchPressCountChanged(m_touchPressCount);
4715- // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself.
4716- // Didn't have time to debug yet.
4717- // Q_EMIT inputMethodRequested();
4718 } else if (event->touchPointStates() & Qt::TouchPointReleased) {
4719 ++m_touchReleaseCount;
4720 Q_EMIT touchReleaseCountChanged(m_touchReleaseCount);
4721
4722=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h'
4723--- tests/mocks/Unity/Application/MirSurfaceItem.h 2015-03-12 14:45:44 +0000
4724+++ tests/mocks/Unity/Application/MirSurfaceItem.h 2015-04-16 12:11:39 +0000
4725@@ -1,5 +1,5 @@
4726 /*
4727- * Copyright (C) 2014 Canonical, Ltd.
4728+ * Copyright (C) 2014-2015 Canonical, Ltd.
4729 *
4730 * This program is free software; you can redistribute it and/or modify
4731 * it under the terms of the GNU General Public License as published by
4732@@ -29,17 +29,18 @@
4733 Q_OBJECT
4734 Q_ENUMS(Type)
4735 Q_ENUMS(State)
4736+ Q_ENUMS(OrientationAngle)
4737
4738 Q_PROPERTY(Type type READ type NOTIFY typeChanged)
4739 Q_PROPERTY(State state READ state NOTIFY stateChanged)
4740 Q_PROPERTY(QString name READ name CONSTANT)
4741 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
4742- Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged DESIGNABLE false)
4743-
4744- Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount NOTIFY touchPressCountChanged
4745- DESIGNABLE false)
4746- Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount NOTIFY touchReleaseCountChanged
4747- DESIGNABLE false)
4748+ Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
4749+ NOTIFY orientationAngleChanged DESIGNABLE false)
4750+ Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount
4751+ NOTIFY touchPressCountChanged DESIGNABLE false)
4752+ Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount
4753+ NOTIFY touchReleaseCountChanged DESIGNABLE false)
4754
4755 public:
4756 enum Type {
4757@@ -62,6 +63,13 @@
4758 Fullscreen,
4759 };
4760
4761+ enum OrientationAngle {
4762+ Angle0 = 0,
4763+ Angle90 = 90,
4764+ Angle180 = 180,
4765+ Angle270 = 270
4766+ };
4767+
4768 ~MirSurfaceItem();
4769
4770 //getters
4771@@ -70,9 +78,9 @@
4772 State state() const { return m_state; }
4773 QString name() const { return m_name; }
4774 bool live() const { return m_live; }
4775- Qt::ScreenOrientation orientation() const { return m_orientation; }
4776+ OrientationAngle orientationAngle() const { return m_orientationAngle; }
4777
4778- void setOrientation(const Qt::ScreenOrientation orientation);
4779+ void setOrientationAngle(OrientationAngle angle);
4780
4781 void setSession(Session* item);
4782 void setScreenshot(const QUrl& screenshot);
4783@@ -91,29 +99,14 @@
4784 void typeChanged(Type);
4785 void stateChanged(State);
4786 void liveChanged(bool live);
4787- void orientationChanged();
4788+ void orientationAngleChanged(OrientationAngle angle);
4789 void touchPressCountChanged(int count);
4790 void touchReleaseCountChanged(int count);
4791
4792- void inputMethodRequested();
4793- void inputMethodDismissed();
4794-
4795 // internal mock use
4796 void deregister();
4797
4798 protected:
4799- void touchEvent(QTouchEvent * event) override;
4800-
4801- // becaue the default implementation ignores the events
4802- void mousePressEvent(QMouseEvent *) override {}
4803- void mouseMoveEvent(QMouseEvent *) override {}
4804- void mouseReleaseEvent(QMouseEvent *) override {}
4805-
4806-private Q_SLOTS:
4807- void onFocusChanged();
4808- void onComponentStatusChanged(QQmlComponent::Status status);
4809-
4810-private:
4811 explicit MirSurfaceItem(const QString& name,
4812 Type type,
4813 State state,
4814@@ -121,6 +114,12 @@
4815 const QString &qmlFilePath = QString(),
4816 QQuickItem *parent = 0);
4817
4818+ void touchEvent(QTouchEvent * event) override;
4819+
4820+private Q_SLOTS:
4821+ void onComponentStatusChanged(QQmlComponent::Status status);
4822+
4823+private:
4824 void createQmlContentItem();
4825 void printComponentErrors();
4826
4827@@ -129,19 +128,20 @@
4828 const Type m_type;
4829 State m_state;
4830 bool m_live;
4831- Qt::ScreenOrientation m_orientation;
4832- int m_touchPressCount;
4833- int m_touchReleaseCount;
4834+ OrientationAngle m_orientationAngle;
4835
4836 QQmlComponent *m_qmlContentComponent;
4837 QQuickItem *m_qmlItem;
4838 QUrl m_screenshotUrl;
4839
4840+ int m_touchPressCount;
4841+ int m_touchReleaseCount;
4842+
4843 friend class SurfaceManager;
4844 };
4845
4846 Q_DECLARE_METATYPE(MirSurfaceItem*)
4847 Q_DECLARE_METATYPE(QList<MirSurfaceItem*>)
4848-Q_DECLARE_METATYPE(Qt::ScreenOrientation)
4849+Q_DECLARE_METATYPE(MirSurfaceItem::OrientationAngle)
4850
4851 #endif // MIRSURFACEITEM_H
4852
4853=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.qml'
4854--- tests/mocks/Unity/Application/MirSurfaceItem.qml 2015-01-28 12:59:21 +0000
4855+++ tests/mocks/Unity/Application/MirSurfaceItem.qml 2015-04-16 12:11:39 +0000
4856@@ -1,5 +1,5 @@
4857 /*
4858- * Copyright 2014 Canonical Ltd.
4859+ * Copyright 2014-2015 Canonical Ltd.
4860 *
4861 * This program is free software; you can redistribute it and/or modify
4862 * it under the terms of the GNU Lesser General Public License as published by
4863@@ -23,19 +23,11 @@
4864 implicitWidth: units.gu(40)
4865 implicitHeight: units.gu(70)
4866
4867- rotation: {
4868- if (orientation == Qt.PortraitOrientation) return 0;
4869- else if (orientation == Qt.LandscapeOrientation) return 90;
4870- else if (orientation == Qt.InvertedPortraitOrientation) return 180;
4871- else return 270;
4872- }
4873- x: parent ? (parent.width - width) / 2 : 0
4874- y: parent ? (parent.height - height) / 2 : 0
4875- width: parent ? (rotation == 0 || rotation == 180 ? parent.width : parent.height) : implicitWidth
4876- height: parent ? (rotation == 0 || rotation == 180 ? parent.height : parent.width) : implicitHeight
4877+ width: parent ? parent.width : implicitWidth
4878+ height: parent ? parent.height : implicitHeight
4879
4880 property alias screenshotSource: screenshotImage.source
4881- property int orientation: Qt.PortraitOrientation
4882+ property int orientationAngle
4883
4884 Image {
4885 id: screenshotImage
4886@@ -50,6 +42,7 @@
4887 fontSizeMode: Text.Fit
4888 minimumPixelSize: 10
4889 verticalAlignment: Text.AlignVCenter
4890+ rotation: surfaceText.rotation
4891 x: surfaceText.x
4892 y: surfaceText.y
4893 width: surfaceText.width
4894@@ -59,12 +52,17 @@
4895 }
4896 Text {
4897 id: surfaceText
4898- anchors.fill: parent
4899- text: "SURFACE"
4900+ text: "SURFACE " + root.width + "," + root.height
4901 color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
4902 font.bold: true
4903 fontSizeMode: Text.Fit
4904 minimumPixelSize: 10; font.pixelSize: 200
4905 verticalAlignment: Text.AlignVCenter
4906+
4907+ rotation: root.orientationAngle
4908+ x: (parent.width - width) / 2
4909+ y: (parent.height - height) / 2
4910+ width: (rotation == 0 || rotation == 180 ? parent.width : parent.height)
4911+ height:(rotation == 0 || rotation == 180 ? parent.height : parent.width)
4912 }
4913 }
4914
4915=== modified file 'tests/mocks/Unity/Application/SurfaceManager.cpp'
4916--- tests/mocks/Unity/Application/SurfaceManager.cpp 2014-08-29 14:50:49 +0000
4917+++ tests/mocks/Unity/Application/SurfaceManager.cpp 2015-04-16 12:11:39 +0000
4918@@ -16,8 +16,9 @@
4919
4920 #include "SurfaceManager.h"
4921
4922+#include "VirtualKeyboard.h"
4923+
4924 #include <paths.h>
4925-#include "MirSurfaceItem.h"
4926
4927 SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
4928
4929@@ -40,10 +41,7 @@
4930 MirSurfaceItem::State state,
4931 const QUrl& screenshot)
4932 {
4933- MirSurfaceItem* surface = new MirSurfaceItem(name,
4934- type,
4935- state,
4936- screenshot);
4937+ MirSurfaceItem* surface = new MirSurfaceItem(name, type, state, screenshot);
4938 Q_EMIT surfaceCreated(surface);
4939 return surface;
4940 }
4941@@ -57,40 +55,12 @@
4942 surface->setLive(false);
4943 Q_EMIT surfaceDestroyed(surface);
4944 });
4945- connect(surface, &MirSurfaceItem::inputMethodRequested,
4946- this, &SurfaceManager::showInputMethod);
4947- connect(surface, &MirSurfaceItem::inputMethodDismissed,
4948- this, &SurfaceManager::hideInputMethod);
4949-}
4950-
4951-void SurfaceManager::showInputMethod()
4952-{
4953- inputMethodSurface()->setState(MirSurfaceItem::Restored);
4954-}
4955-
4956-void SurfaceManager::hideInputMethod()
4957-{
4958- if (m_virtualKeyboard) {
4959- m_virtualKeyboard->setState(MirSurfaceItem::Minimized);
4960- }
4961 }
4962
4963 MirSurfaceItem *SurfaceManager::inputMethodSurface()
4964 {
4965 if (!m_virtualKeyboard) {
4966-
4967- QString screenshotPath = QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png")
4968- .arg(qmlDirectory());
4969-
4970- QString qmlFilePath = QString("%1/Unity/Application/VirtualKeyboard.qml")
4971- .arg(mockPluginsDir());
4972-
4973- m_virtualKeyboard = new MirSurfaceItem(
4974- "input-method",
4975- MirSurfaceItem::InputMethod,
4976- MirSurfaceItem::Minimized,
4977- screenshotPath,
4978- qmlFilePath);
4979+ m_virtualKeyboard = new VirtualKeyboard;
4980 Q_EMIT surfaceCreated(m_virtualKeyboard);
4981 }
4982 return m_virtualKeyboard;
4983
4984=== modified file 'tests/mocks/Unity/Application/SurfaceManager.h'
4985--- tests/mocks/Unity/Application/SurfaceManager.h 2014-08-28 23:16:07 +0000
4986+++ tests/mocks/Unity/Application/SurfaceManager.h 2015-04-16 12:11:39 +0000
4987@@ -20,6 +20,7 @@
4988 #include <QObject>
4989
4990 #include "MirSurfaceItem.h"
4991+#include "VirtualKeyboard.h"
4992
4993 class SurfaceManager : public QObject
4994 {
4995@@ -45,13 +46,9 @@
4996 void surfaceCreated(MirSurfaceItem *surface);
4997 void surfaceDestroyed(MirSurfaceItem *surface);
4998
4999-private Q_SLOTS:
5000- void showInputMethod();
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches