Merge lp:~gerboland/unity8/orientationLock into lp:unity8

Proposed by Gerry Boland
Status: Merged
Approved by: Daniel d'Andrada
Approved revision: 1222
Merged at revision: 1264
Proposed branch: lp:~gerboland/unity8/orientationLock
Merge into: lp:unity8
Diff against target: 741 lines (+382/-6)
17 files modified
plugins/Unity/Session/CMakeLists.txt (+7/-0)
plugins/Unity/Session/orientationlock.cpp (+74/-0)
plugins/Unity/Session/orientationlock.h (+62/-0)
plugins/Unity/Session/plugin.cpp (+8/-4)
qml/Shell.qml (+25/-0)
qml/Stages/ApplicationWindow.qml (+2/-0)
qml/Stages/PhoneStage.qml (+9/-0)
qml/Stages/SessionContainer.qml (+7/-0)
qml/Stages/SpreadDelegate.qml (+2/-0)
qml/Stages/SurfaceContainer.qml (+5/-0)
qml/Stages/TabletStage.qml (+8/-0)
tests/mocks/Unity/Application/MirSurfaceItem.cpp (+14/-0)
tests/mocks/Unity/Application/MirSurfaceItem.h (+7/-0)
tests/mocks/Unity/Application/MirSurfaceItem.qml (+12/-2)
tests/qmltests/Stages/tst_ApplicationWindow.qml (+19/-0)
tests/qmltests/Stages/tst_PhoneStage.qml (+58/-0)
tests/qmltests/Stages/tst_SessionContainer.qml (+63/-0)
To merge this branch: bzr merge lp:~gerboland/unity8/orientationLock
Reviewer Review Type Date Requested Status
Daniel d'Andrada (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+232484@code.launchpad.net

Commit message

Shell-controlled orientation support with orientation lock ability

Description of the change

Shell-controlled orientation support with orientation lock ability

 * Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~gerboland/qtmir/exposeOrientation/+merge/232485
https://code.launchpad.net/~gerboland/qtubuntu/exposeOrientation/+merge/232252
https://code.launchpad.net/~gerboland/platform-api/exposeOrientation/+merge/232250
 * Did you perform an exploratory manual test run of your code change and any related functionality?
Y
 * Did you make sure that your branch does not contain spurious tags?
Y
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
 * If you changed the UI, has there been a design review?
N

To post a comment you must log in.
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

270 + surface.orientation = Qt.binding( function() { return root.orientation; } );

Use a Binding component instead, like it's already done for surface.enabled and surface.interactive.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

OrientationLock::savedOrientation is not being used anywhere.

----------------------------------------------------------------

In plugins/Unity/Session/plugin.cpp:

"""
-static QObject *dbusunitysessionservice_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
+static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
 {
- Q_UNUSED(engine)
- Q_UNUSED(scriptEngine)
-
"""

Why? Is using Q_UNUSED() bad form?

---------------------------------------------------------------------

In qml/Shell.qml

"""
+ readonly property int deviceOrientation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
"""

Please call it "orientation angle" or "rotation" instead of "orientation" as it's not a Qt.ScreenOrientation type.

---------------------------------------------------------------------

In qml/Stages/TabletStage.qml:

"""
+ onInteractiveChanged: {
+ if (interactive) {
+ appDelegate.orientation = Qt.binding( function() { return root.orientation; } );
+ } else {
+ appDelegate.orientation = root.orientation; // breaks the binding intentionally
+ }
+ }
"""

Could it use the same approach as qml/Stages/PhoneStage.qml?

---------------------------------------------------------------------

Run "make tryShell", "make tryApplicationWindow" or "make trySpreadDelegate". The "SURFACE" word is rotated.

---------------------------------------------------------------------

Please rotate the whole MirSurfaceItem mock and not just its text. Like this:
http://paste.ubuntu.com/8292833/

-----------------------------------------------------------------------

Add a test in SessionContainer that verifies that child sessions rotate along with the root one.

In qml/Stages/ApplicationWindow.qml:

"""
                surface.orientation = Qt.binding( function() { return root.orientation; } );
"""

Please make it a Binding{} and put it alongside other similar bindings in SessionContainer.qml
Then ApplicationWindow.orientation would be just an alias to its internal SessionContainer.orientation.

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

> [...]
> Please call it "orientation angle" or "rotation" instead of "orientation" as
> it's not a Qt.ScreenOrientation type.
> [...]

Actually "rotation" would clash with Item.rotation.

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

By the way, have you checked how does that work with the virtual keyboard? eg: if you lock the app orientation, will the vkb keep rotating?

lp:~gerboland/unity8/orientationLock updated
1212. By Gerry Boland

Merge trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~gerboland/unity8/orientationLock updated
1213. By Gerry Boland

Use OrientationLock.savedOrientation. Rename deviceOrientation to deviceOrientationAngle

1214. By Gerry Boland

TabletStage: Use Binding instead of JS settings a binding

1215. By Gerry Boland

MockMirSurfaceItem: Rotate whole thing, even if image looks weird

1216. By Gerry Boland

Use Binding instead JS establised binding

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~gerboland/unity8/orientationLock updated
1217. By Gerry Boland

Propagate orientation to all child surfaces, update tests to suit

1218. By Gerry Boland

Oopsie, orientation will work now

1219. By Gerry Boland

Add test for SurfaceContainer

Revision history for this message
Gerry Boland (gerboland) wrote :

> OrientationLock::savedOrientation is not being used anywhere.
I added code to use it, but it's not persistent over logout/in yet so is a touch pointless. I wanted to implement the persistence in a separate MR, but I can do it now if you like.

> ----------------------------------------------------------------
>
> In plugins/Unity/Session/plugin.cpp:
>
> """
> -static QObject *dbusunitysessionservice_provider(QQmlEngine *engine,
> QJSEngine *scriptEngine)
> +static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/,
> QJSEngine */*jsEngine*/)
> {
> - Q_UNUSED(engine)
> - Q_UNUSED(scriptEngine)
> -
> """
>
> Why? Is using Q_UNUSED() bad form?

I recall Saviq saying he preferred this to Q_UNUSED. Depends on how clever the compiler is really - if we don't name the argument then the compiler definitely knows not to bother with it. But if we use Q_UNUSED it depends on if the compiler is smart enough to guess our intentions. I prefer this way too really.

> ---------------------------------------------------------------------
>
> In qml/Shell.qml
>
> """
> + readonly property int deviceOrientation:
> Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
> """
>
> Please call it "orientation angle" or "rotation" instead of "orientation" as
> it's not a Qt.ScreenOrientation type.

Done.

> ---------------------------------------------------------------------
>
> In qml/Stages/TabletStage.qml:
>
> """
> + onInteractiveChanged: {
> + if (interactive) {
> + appDelegate.orientation = Qt.binding( function()
> { return root.orientation; } );
> + } else {
> + appDelegate.orientation = root.orientation; //
> breaks the binding intentionally
> + }
> + }
> """
>
> Could it use the same approach as qml/Stages/PhoneStage.qml?

Done

>
> ---------------------------------------------------------------------
>
> Run "make tryShell", "make tryApplicationWindow" or "make trySpreadDelegate".
> The "SURFACE" word is rotated.
>
> ---------------------------------------------------------------------
>
> Please rotate the whole MirSurfaceItem mock and not just its text. Like this:
> http://paste.ubuntu.com/8292833/

The orientation of the word was my hint to tester the orientation of the mock surface - I thought rotating the image and scaling it was more ugly. But I did it your way, and yeah it's more clear what's going on.

> -----------------------------------------------------------------------
>
> Add a test in SessionContainer that verifies that child sessions rotate along
> with the root one.

Done

> In qml/Stages/ApplicationWindow.qml:
>
> """
> surface.orientation = Qt.binding( function() { return
> root.orientation; } );
> """
>
> Please make it a Binding{} and put it alongside other similar bindings in
> SessionContainer.qml
> Then ApplicationWindow.orientation would be just an alias to its internal
> SessionContainer.orientation.

Done

Revision history for this message
Gerry Boland (gerboland) wrote :

> By the way, have you checked how does that work with the virtual keyboard? eg:
> if you lock the app orientation, will the vkb keep rotating?

Application tells the keyboard what geometry to be, so it always suits what orientation the app is in. If orientation locked, app orientation does not change, and so keyboard doesn't either.

lp:~gerboland/unity8/orientationLock updated
1220. By Gerry Boland

Bad copy/paste, fix orientation on tablet

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

In tests/qmltests/Stages/tst_SessionContainer.qml

"""
                tryCompare(sessionContainer, "orientation", sessionContainer.orientation);
"""

Huh!? You're comparing sessionContainer.orientation to itself?
You probably meant:
tryCompare(childContainer, "orientation", sessionContainer.orientation);

Calling it rootSessionContainer instead of just sessionContainer would also help to clarify things. But that's just a suggestion.

-----------------------------------------------------

In tests/qmltests/Stages/tst_SessionContainer.qml

"""
/* Text orientation changes are propagated to all children immediately */
"""

typo. s/Text/Test

------------------------------------------------------

"""
--- qml/Stages/ApplicationWindow.qml 2014-09-03 16:38:10 +0000
+++ qml/Stages/ApplicationWindow.qml 2014-09-11 10:41:11 +0000
@@ -27,6 +27,7 @@ Item {

     // to be set from outside
     property QtObject application
+ property int orientation

     QtObject {
         id: d
@@ -111,6 +112,7 @@ Item {
         id: sessionContainer
         session: application ? application.session : null
         anchors.fill: parent
+ orientation: root.orientation

         onSurfaceChanged: {
             if (sessionContainer.surface) {
"""

Just a suggestion (feel free to change it or not):
I think it's better to use property aliases in such cases as they seem to be more efficient (besides being more concise) than property bindings. But who knows, maybe they boil down to the same in the qml engine...

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

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

* Did CI run pass? If not, please explain why.
No but due to an unrelated failure.

Revision history for this message
Gerry Boland (gerboland) wrote :

> In tests/qmltests/Stages/tst_SessionContainer.qml
>
> """
> tryCompare(sessionContainer, "orientation",
> sessionContainer.orientation);
> """
>
> Huh!? You're comparing sessionContainer.orientation to itself?
> You probably meant:
> tryCompare(childContainer, "orientation", sessionContainer.orientation);

Yep silly me. Thanks for catching that.

> Calling it rootSessionContainer instead of just sessionContainer would also
> help to clarify things. But that's just a suggestion.

Done. And used childSessionContainer while I was at it.

> -----------------------------------------------------
>
> In tests/qmltests/Stages/tst_SessionContainer.qml
>
> """
> /* Text orientation changes are propagated to all children immediately */
> """
>
> typo. s/Text/Test

Done

> ------------------------------------------------------
>
> """
> --- qml/Stages/ApplicationWindow.qml 2014-09-03 16:38:10 +0000
> +++ qml/Stages/ApplicationWindow.qml 2014-09-11 10:41:11 +0000
> @@ -27,6 +27,7 @@ Item {
>
> // to be set from outside
> property QtObject application
> + property int orientation
>
> QtObject {
> id: d
> @@ -111,6 +112,7 @@ Item {
> id: sessionContainer
> session: application ? application.session : null
> anchors.fill: parent
> + orientation: root.orientation
>
> onSurfaceChanged: {
> if (sessionContainer.surface) {
> """
>
> Just a suggestion (feel free to change it or not):
> I think it's better to use property aliases in such cases as they seem to be
> more efficient (besides being more concise) than property bindings. But who
> knows, maybe they boil down to the same in the qml engine...

I've no idea how aliases work. I suppose they must be more efficient, but I also heard from a Qt developer (working on the QtQuickComponents) that they're expensive. So I don't know! I tend not to use them as (in this instance) it's harder to see immediately that SessionContainer has an orientation property, and you need to scroll around to figure out what sets that property. Personal taste I guess :) Mind if I leave it alone? If we learn it is indeed an optimization, I'll switch it.

lp:~gerboland/unity8/orientationLock updated
1221. By Gerry Boland

test_SessionContainer: stupid compare statements fixed. Using root & child prefixes to clarify things

1222. By Gerry Boland

Typo

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

Thumbs up.

--------------------

"""
tryCompare(rootSessionContainer, "orientation", childSessionContainer.orientation);
"""

Nitpick:
You should swap rootSessionContainer with childSessionContainer, as the last parameter is the expected value.
Ie, you expect childSessionContainer.orientation (the property whose value should change) to eventually be rootSessionContainer.orientation (the value that you've manually set), not the other way round.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/Unity/Session/CMakeLists.txt'
2--- plugins/Unity/Session/CMakeLists.txt 2014-06-13 13:13:16 +0000
3+++ plugins/Unity/Session/CMakeLists.txt 2014-09-12 19:54:38 +0000
4@@ -1,10 +1,16 @@
5+include(FindPkgConfig)
6+
7+pkg_search_module(GIO REQUIRED gio-2.0)
8+
9 include_directories(
10 ${CMAKE_CURRENT_SOURCE_DIR}
11+ ${GIO_INCLUDE_DIRS}
12 )
13
14 set(QMLSESSIONPLUGIN_SRC
15 plugin.cpp
16 dbusunitysessionservice.cpp
17+ orientationlock.cpp
18 )
19
20 add_library(UnitySession-qml MODULE
21@@ -12,6 +18,7 @@
22 )
23
24 qt5_use_modules(UnitySession-qml DBus Qml)
25+target_link_libraries(UnitySession-qml ${GIO_LDFLAGS})
26
27 # export the qmldir and qmltypes files
28 add_unity8_plugin(Unity.Session 0.1 Unity/Session TARGETS UnitySession-qml)
29
30=== added file 'plugins/Unity/Session/orientationlock.cpp'
31--- plugins/Unity/Session/orientationlock.cpp 1970-01-01 00:00:00 +0000
32+++ plugins/Unity/Session/orientationlock.cpp 2014-09-12 19:54:38 +0000
33@@ -0,0 +1,74 @@
34+/*
35+ * Copyright (C) 2014 Canonical, Ltd.
36+ *
37+ * This program is free software: you can redistribute it and/or modify it under
38+ * the terms of the GNU Lesser General Public License version 3, as published by
39+ * the Free Software Foundation.
40+ *
41+ * This program is distributed in the hope that it will be useful, but WITHOUT
42+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
43+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44+ * Lesser General Public License for more details.
45+ *
46+ * You should have received a copy of the GNU Lesser General Public License
47+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
48+ */
49+
50+#include "orientationlock.h"
51+
52+#include <QDBusConnection>
53+#include <QDBusInterface>
54+
55+OrientationLock::OrientationLock(QObject *parent)
56+ : QObject(parent)
57+ , m_enabled(false)
58+ , m_savedOrientation(Qt::PortraitOrientation)
59+{
60+ systemSettings = g_settings_new("com.ubuntu.touch.system");
61+ g_signal_connect(systemSettings, "changed::rotation-lock",
62+ G_CALLBACK(OrientationLock::onEnabledChangedProxy), this);
63+ m_enabled = g_settings_get_boolean(systemSettings, "rotation-lock");
64+}
65+
66+OrientationLock::~OrientationLock()
67+{
68+ g_signal_handlers_disconnect_by_data(systemSettings, this);
69+ g_object_unref(systemSettings);
70+}
71+
72+bool OrientationLock::enabled() const
73+{
74+ return m_enabled;
75+}
76+
77+Qt::ScreenOrientation OrientationLock::savedOrientation() const
78+{
79+ return m_savedOrientation;
80+}
81+
82+void OrientationLock::onEnabledChangedProxy(GSettings */*settings*/, const gchar */*key*/, gpointer data)
83+{
84+ OrientationLock* _this = static_cast<OrientationLock*>(data);
85+ _this->onEnabledChanged();
86+}
87+
88+void OrientationLock::onEnabledChanged()
89+{
90+ const bool enabled = g_settings_get_boolean(systemSettings, "rotation-lock");
91+ if (m_enabled != enabled) {
92+ m_enabled = enabled;
93+ Q_EMIT enabledChanged();
94+ }
95+}
96+
97+void OrientationLock::setSavedOrientation(const Qt::ScreenOrientation orientation)
98+{
99+ if (orientation == m_savedOrientation) {
100+ return;
101+ }
102+
103+ m_savedOrientation = orientation;
104+
105+ //TODO - save value with dbus to persist over sessions
106+ Q_EMIT savedOrientationChanged();
107+}
108
109=== added file 'plugins/Unity/Session/orientationlock.h'
110--- plugins/Unity/Session/orientationlock.h 1970-01-01 00:00:00 +0000
111+++ plugins/Unity/Session/orientationlock.h 2014-09-12 19:54:38 +0000
112@@ -0,0 +1,62 @@
113+/*
114+ * Copyright (C) 2014 Canonical, Ltd.
115+ *
116+ * This program is free software: you can redistribute it and/or modify it under
117+ * the terms of the GNU Lesser General Public License version 3, as published by
118+ * the Free Software Foundation.
119+ *
120+ * This program is distributed in the hope that it will be useful, but WITHOUT
121+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
122+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
123+ * Lesser General Public License for more details.
124+ *
125+ * You should have received a copy of the GNU Lesser General Public License
126+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
127+ */
128+
129+#ifndef ORIENTATIONLOCK_H
130+#define ORIENTATIONLOCK_H
131+
132+#include <gio/gio.h>
133+#include <QtCore/QObject>
134+#include <QtDBus/QDBusInterface>
135+
136+/**
137+ * @brief The OrientationLock class exports orientation lock related properties to QML
138+ * It has two properties:
139+ * - readonly boolean with the Orientation lock property state
140+ * - Qt::ScreenOrientation to save the locked orientation state across Sessions (only relevant if lock is true)
141+ */
142+class OrientationLock : public QObject
143+{
144+ Q_OBJECT
145+
146+ Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
147+ Q_PROPERTY(Qt::ScreenOrientation savedOrientation READ savedOrientation WRITE setSavedOrientation
148+ NOTIFY savedOrientationChanged)
149+
150+public:
151+ explicit OrientationLock(QObject *parent = 0);
152+ ~OrientationLock();
153+
154+ bool enabled() const;
155+ Qt::ScreenOrientation savedOrientation() const;
156+ void setSavedOrientation(const Qt::ScreenOrientation orientation);
157+
158+Q_SIGNALS:
159+ void enabledChanged();
160+ void savedOrientationChanged();
161+
162+private Q_SLOTS:
163+ static void onEnabledChangedProxy(GSettings *settings, const gchar *key, gpointer data);
164+ void onEnabledChanged();
165+
166+private:
167+ QDBusInterface *dbusInterface;
168+ GSettings *systemSettings;
169+
170+ bool m_enabled;
171+ Qt::ScreenOrientation m_savedOrientation;
172+};
173+
174+#endif // ORIENTATIONLOCK_H
175
176=== modified file 'plugins/Unity/Session/plugin.cpp'
177--- plugins/Unity/Session/plugin.cpp 2014-05-07 15:21:51 +0000
178+++ plugins/Unity/Session/plugin.cpp 2014-09-12 19:54:38 +0000
179@@ -18,23 +18,27 @@
180
181 #include "plugin.h"
182 #include "dbusunitysessionservice.h"
183+#include "orientationlock.h"
184
185 #include <QAbstractItemModel>
186 #include <QDBusConnection>
187 #include <QtQml/qqml.h>
188
189-static QObject *dbusunitysessionservice_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
190+static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
191 {
192- Q_UNUSED(engine)
193- Q_UNUSED(scriptEngine)
194-
195 return new DBusUnitySessionService();
196 }
197
198+static QObject *orientationlock_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
199+{
200+ return new OrientationLock();
201+}
202+
203 void SessionPlugin::registerTypes(const char *uri)
204 {
205 qmlRegisterType<QAbstractItemModel>();
206
207 Q_ASSERT(uri == QLatin1String("Unity.Session"));
208 qmlRegisterSingletonType<DBusUnitySessionService>(uri, 0, 1, "DBusUnitySessionService", dbusunitysessionservice_provider);
209+ qmlRegisterSingletonType<OrientationLock>(uri, 0, 1, "OrientationLock", orientationlock_provider);
210 }
211
212=== modified file 'qml/Shell.qml'
213--- qml/Shell.qml 2014-09-08 14:15:51 +0000
214+++ qml/Shell.qml 2014-09-12 19:54:38 +0000
215@@ -15,6 +15,7 @@
216 */
217
218 import QtQuick 2.0
219+import QtQuick.Window 2.0
220 import AccountsService 0.1
221 import GSettings 1.0
222 import Unity.Application 0.1
223@@ -61,6 +62,22 @@
224 property int failedLoginsDelayAttempts: 7 // number of failed logins
225 property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
226
227+ property int orientation
228+ readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
229+ onDeviceOrientationAngleChanged: {
230+ if (!OrientationLock.enabled) {
231+ orientation = Screen.orientation;
232+ }
233+ }
234+ readonly property bool orientationLockEnabled: OrientationLock.enabled
235+ onOrientationLockEnabledChanged: {
236+ if (orientationLockEnabled) {
237+ OrientationLock.savedOrientation = Screen.orientation;
238+ } else {
239+ orientation = Screen.orientation;
240+ }
241+ }
242+
243 function activateApplication(appId) {
244 if (ApplicationManager.findApplication(appId)) {
245 ApplicationManager.requestFocusApplication(appId);
246@@ -85,6 +102,9 @@
247
248 Component.onCompleted: {
249 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
250+ if (orientationLockEnabled) {
251+ orientation = OrientationLock.savedOrientation;
252+ }
253 }
254
255 GSettings {
256@@ -207,6 +227,11 @@
257 property: "inverseProgress"
258 value: launcher.progress
259 }
260+ Binding {
261+ target: applicationsDisplayLoader.item
262+ property: "orientation"
263+ value: shell.orientation
264+ }
265 }
266 }
267
268
269=== modified file 'qml/Stages/ApplicationWindow.qml'
270--- qml/Stages/ApplicationWindow.qml 2014-09-03 16:38:10 +0000
271+++ qml/Stages/ApplicationWindow.qml 2014-09-12 19:54:38 +0000
272@@ -27,6 +27,7 @@
273
274 // to be set from outside
275 property QtObject application
276+ property int orientation
277
278 QtObject {
279 id: d
280@@ -111,6 +112,7 @@
281 id: sessionContainer
282 session: application ? application.session : null
283 anchors.fill: parent
284+ orientation: root.orientation
285
286 onSurfaceChanged: {
287 if (sessionContainer.surface) {
288
289=== modified file 'qml/Stages/PhoneStage.qml'
290--- qml/Stages/PhoneStage.qml 2014-09-02 14:57:05 +0000
291+++ qml/Stages/PhoneStage.qml 2014-09-12 19:54:38 +0000
292@@ -18,6 +18,7 @@
293 import Ubuntu.Components 0.1
294 import Ubuntu.Gestures 0.1
295 import Unity.Application 0.1
296+import Unity.Session 0.1
297 import Utils 0.1
298 import "../Components"
299
300@@ -30,6 +31,7 @@
301 property bool interactive
302 property bool spreadEnabled: true // If false, animations and right edge will be disabled
303 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
304+ property int orientation: Qt.PortraitOrientation
305
306 color: "black"
307
308@@ -372,6 +374,13 @@
309 progress: appDelegate.progress - spreadView.positionMarker1
310 }
311
312+ Binding {
313+ target: appDelegate
314+ property: "orientation"
315+ when: appDelegate.interactive
316+ value: root.orientation
317+ }
318+
319 onClicked: {
320 if (spreadView.phase == 2) {
321 if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
322
323=== modified file 'qml/Stages/SessionContainer.qml'
324--- qml/Stages/SessionContainer.qml 2014-09-02 15:51:04 +0000
325+++ qml/Stages/SessionContainer.qml 2014-09-12 19:54:38 +0000
326@@ -24,12 +24,14 @@
327 readonly property var childSessions: session ? session.childSessions : null
328 readonly property alias surface: _surfaceContainer.surface
329 property bool interactive: true
330+ property int orientation
331
332 readonly property alias surfaceContainer: _surfaceContainer
333 SurfaceContainer {
334 id: _surfaceContainer
335 anchors.fill: parent
336 surface: session ? session.surface : null
337+ orientation: root.orientation
338 }
339
340 Binding {
341@@ -74,6 +76,11 @@
342 target: item; when: item
343 property: "height"; value: root.height
344 }
345+
346+ Binding {
347+ target: item; when: item
348+ property: "orientation"; value: root.orientation
349+ }
350 }
351 }
352
353
354=== modified file 'qml/Stages/SpreadDelegate.qml'
355--- qml/Stages/SpreadDelegate.qml 2014-08-27 20:58:10 +0000
356+++ qml/Stages/SpreadDelegate.qml 2014-09-12 19:54:38 +0000
357@@ -36,6 +36,7 @@
358 property alias swipeToCloseEnabled: dragArea.enabled
359 property bool closeable
360 property alias application: appWindow.application
361+ property int orientation
362
363 Item {
364 objectName: "appWindowWithShadow"
365@@ -62,6 +63,7 @@
366 }
367
368 interactive: root.interactive
369+ orientation: root.orientation
370 }
371 }
372
373
374=== modified file 'qml/Stages/SurfaceContainer.qml'
375--- qml/Stages/SurfaceContainer.qml 2014-09-01 12:48:57 +0000
376+++ qml/Stages/SurfaceContainer.qml 2014-09-12 19:54:38 +0000
377@@ -22,6 +22,7 @@
378 objectName: "surfaceContainer"
379 property Item surface: null
380 property bool hadSurface: false
381+ property int orientation
382
383 onSurfaceChanged: {
384 if (surface) {
385@@ -34,6 +35,10 @@
386 target: surface
387 property: "anchors.fill"; value: root
388 }
389+ Binding {
390+ target: surface
391+ property: "orientation"; value: root.orientation
392+ }
393
394 states: [
395 State {
396
397=== modified file 'qml/Stages/TabletStage.qml'
398--- qml/Stages/TabletStage.qml 2014-09-02 18:10:49 +0000
399+++ qml/Stages/TabletStage.qml 2014-09-12 19:54:38 +0000
400@@ -34,6 +34,7 @@
401 property real maximizedAppTopMargin
402 property bool interactive
403 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
404+ property int orientation: Qt.PortraitOrientation
405
406 onInverseProgressChanged: {
407 // This can't be a simple binding because that would be triggered after this handler
408@@ -535,6 +536,13 @@
409 return progress;
410 }
411
412+ Binding {
413+ target: spreadTile
414+ property: "orientation"
415+ when: spreadTile.interactive
416+ value: root.orientation
417+ }
418+
419 onClicked: {
420 if (spreadView.phase == 2) {
421 spreadView.snapTo(index);
422
423=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp'
424--- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2014-08-29 14:50:49 +0000
425+++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2014-09-12 19:54:38 +0000
426@@ -39,6 +39,7 @@
427 , m_type(type)
428 , m_state(state)
429 , m_live(true)
430+ , m_orientation(Qt::PortraitOrientation)
431 , m_qmlItem(nullptr)
432 , m_screenshotUrl(screenshot)
433 {
434@@ -108,6 +109,19 @@
435 }
436 }
437
438+void MirSurfaceItem::setOrientation(const Qt::ScreenOrientation orientation)
439+{
440+ if (m_orientation == orientation)
441+ return;
442+
443+ m_orientation = orientation;
444+
445+ QQmlProperty orientationProp(m_qmlItem, "orientation");
446+ orientationProp.write(QVariant::fromValue(orientation));
447+
448+ Q_EMIT orientationChanged();
449+}
450+
451 void MirSurfaceItem::setSession(Session* session)
452 {
453 m_session = session;
454
455=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h'
456--- tests/mocks/Unity/Application/MirSurfaceItem.h 2014-09-01 12:24:06 +0000
457+++ tests/mocks/Unity/Application/MirSurfaceItem.h 2014-09-12 19:54:38 +0000
458@@ -34,6 +34,7 @@
459 Q_PROPERTY(State state READ state NOTIFY stateChanged)
460 Q_PROPERTY(QString name READ name CONSTANT)
461 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
462+ Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged DESIGNABLE false)
463
464 public:
465 enum Type {
466@@ -64,6 +65,9 @@
467 State state() const { return m_state; }
468 QString name() const { return m_name; }
469 bool live() const { return m_live; }
470+ Qt::ScreenOrientation orientation() const { return m_orientation; }
471+
472+ void setOrientation(const Qt::ScreenOrientation orientation);
473
474 void setSession(Session* item);
475 void setScreenshot(const QUrl& screenshot);
476@@ -76,6 +80,7 @@
477 void typeChanged(Type);
478 void stateChanged(State);
479 void liveChanged(bool live);
480+ void orientationChanged();
481
482 void inputMethodRequested();
483 void inputMethodDismissed();
484@@ -104,6 +109,7 @@
485 const Type m_type;
486 State m_state;
487 bool m_live;
488+ Qt::ScreenOrientation m_orientation;
489
490 QQmlComponent *m_qmlContentComponent;
491 QQuickItem *m_qmlItem;
492@@ -114,5 +120,6 @@
493
494 Q_DECLARE_METATYPE(MirSurfaceItem*)
495 Q_DECLARE_METATYPE(QList<MirSurfaceItem*>)
496+Q_DECLARE_METATYPE(Qt::ScreenOrientation)
497
498 #endif // MIRSURFACEITEM_H
499
500=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.qml'
501--- tests/mocks/Unity/Application/MirSurfaceItem.qml 2014-08-18 16:59:08 +0000
502+++ tests/mocks/Unity/Application/MirSurfaceItem.qml 2014-09-12 19:54:38 +0000
503@@ -21,12 +21,22 @@
504 id: root
505 color: "pink"
506
507- anchors.fill: parent
508-
509 implicitWidth: units.gu(40)
510 implicitHeight: units.gu(70)
511
512+ rotation: {
513+ if (orientation == Qt.PortraitOrientation) return 0;
514+ else if (orientation == Qt.LandscapeOrientation) return 90;
515+ else if (orientation == Qt.InvertedPortraitOrientation) return 180;
516+ else return 270;
517+ }
518+ x: parent ? (parent.width - width) / 2 : 0
519+ y: parent ? (parent.height - height) / 2 : 0
520+ width: parent ? (rotation == 0 || rotation == 180 ? parent.width : parent.height) : implicitWidth
521+ height: parent ? (rotation == 0 || rotation == 180 ? parent.height : parent.width) : implicitHeight
522+
523 property alias screenshotSource: screenshotImage.source
524+ property int orientation: Qt.PortraitOrientation
525
526 property bool wantInputMethod: false
527
528
529=== modified file 'tests/qmltests/Stages/tst_ApplicationWindow.qml'
530--- tests/qmltests/Stages/tst_ApplicationWindow.qml 2014-09-02 23:25:33 +0000
531+++ tests/qmltests/Stages/tst_ApplicationWindow.qml 2014-09-12 19:54:38 +0000
532@@ -50,6 +50,7 @@
533 ApplicationWindow {
534 anchors.fill: parent
535 application: fakeApplication
536+ orientation: Qt.PortraitOrientation
537 }
538 }
539 FocusScope {
540@@ -147,6 +148,24 @@
541 }
542 }
543
544+ Button {
545+ anchors { left: parent.left; right: parent.right }
546+ text: "Rotate device \u27F3"
547+ onClicked: {
548+ var orientation = applicationWindowLoader.item.orientation
549+ if (orientation == Qt.PortraitOrientation) {
550+ orientation = Qt.LandscapeOrientation;
551+ } else if (orientation == Qt.LandscapeOrientation) {
552+ orientation = Qt.InvertedPortraitOrientation;
553+ } else if (orientation == Qt.InvertedPortraitOrientation) {
554+ orientation = Qt.InvertedLandscapeOrientation;
555+ } else {
556+ orientation = Qt.PortraitOrientation;
557+ }
558+ applicationWindowLoader.item.orientation = orientation;
559+ }
560+ }
561+
562 }
563 }
564
565
566=== modified file 'tests/qmltests/Stages/tst_PhoneStage.qml'
567--- tests/qmltests/Stages/tst_PhoneStage.qml 2014-08-27 21:39:17 +0000
568+++ tests/qmltests/Stages/tst_PhoneStage.qml 2014-09-12 19:54:38 +0000
569@@ -31,6 +31,8 @@
570 anchors { fill: parent; rightMargin: units.gu(30) }
571 dragAreaWidth: units.gu(2)
572 maximizedAppTopMargin: units.gu(3) + units.dp(2)
573+ interactive: true
574+ orientation: Qt.PortraitOrientation
575 }
576
577 Binding {
578@@ -67,6 +69,21 @@
579 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
580 }
581 }
582+ Button {
583+ anchors { left: parent.left; right: parent.right }
584+ text: "Rotate device \u27F3"
585+ onClicked: {
586+ if (phoneStage.orientation == Qt.PortraitOrientation) {
587+ phoneStage.orientation = Qt.LandscapeOrientation;
588+ } else if (phoneStage.orientation == Qt.LandscapeOrientation) {
589+ phoneStage.orientation = Qt.InvertedPortraitOrientation;
590+ } else if (phoneStage.orientation == Qt.InvertedPortraitOrientation) {
591+ phoneStage.orientation = Qt.InvertedLandscapeOrientation;
592+ } else {
593+ phoneStage.orientation = Qt.PortraitOrientation;
594+ }
595+ }
596+ }
597 }
598 ListView {
599 id: appList
600@@ -256,6 +273,46 @@
601 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
602 }
603
604+ function test_orientation_change_sent_to_focused_app() {
605+ phoneStage.orientation = Qt.PortraitOrientation;
606+ addApps(1);
607+
608+ var spreadView = findChild(phoneStage, "spreadView");
609+ var app = findChild(spreadView, "appDelegate0");
610+ tryCompare(app, "orientation", Qt.PortraitOrientation);
611+
612+ phoneStage.orientation = Qt.LandscapeOrientation;
613+ tryCompare(app, "orientation", Qt.LandscapeOrientation);
614+ }
615+
616+ function test_orientation_change_not_sent_to_apps_while_spread_open() {
617+ phoneStage.orientation = Qt.PortraitOrientation;
618+ addApps(1);
619+
620+ var spreadView = findChild(phoneStage, "spreadView");
621+ var app = findChild(spreadView, "appDelegate0");
622+ tryCompare(app, "orientation", Qt.PortraitOrientation);
623+
624+ goToSpread();
625+ phoneStage.orientation = Qt.LandscapeOrientation;
626+ tryCompare(app, "orientation", Qt.PortraitOrientation);
627+ }
628+
629+ function test_orientation_change_not_sent_to_unfocused_app_until_it_focused() {
630+ phoneStage.orientation = Qt.PortraitOrientation;
631+ addApps(1);
632+
633+ var spreadView = findChild(phoneStage, "spreadView");
634+ var app = findChild(spreadView, "appDelegate0");
635+
636+ goToSpread();
637+ phoneStage.orientation = Qt.LandscapeOrientation;
638+ tryCompare(app, "orientation", Qt.PortraitOrientation);
639+
640+ phoneStage.select(app.application.appId);
641+ tryCompare(app, "orientation", Qt.LandscapeOrientation);
642+ }
643+
644 function cleanup() {
645 while (ApplicationManager.count > 1) {
646 var oldCount = ApplicationManager.count;
647@@ -263,6 +320,7 @@
648 ApplicationManager.stopApplication(ApplicationManager.get(closingIndex).appId)
649 tryCompare(ApplicationManager, "count", oldCount - 1)
650 }
651+ phoneStage.orientation = Qt.PortraitOrientation;
652 }
653 }
654 }
655
656=== modified file 'tests/qmltests/Stages/tst_SessionContainer.qml'
657--- tests/qmltests/Stages/tst_SessionContainer.qml 2014-09-02 15:51:20 +0000
658+++ tests/qmltests/Stages/tst_SessionContainer.qml 2014-09-12 19:54:38 +0000
659@@ -41,6 +41,7 @@
660 SessionContainer {
661 id: sessionContainer
662 anchors.fill: parent
663+ orientation: Qt.PortraitOrientation
664 }
665 }
666
667@@ -112,6 +113,24 @@
668 session: sessionContainerLoader.item ? sessionContainerLoader.item.session : null
669 }
670 }
671+
672+ Button {
673+ anchors { left: parent.left; right: parent.right }
674+ text: "Rotate device \u27F3"
675+ onClicked: {
676+ var orientation = sessionContainerLoader.item.orientation
677+ if (orientation == Qt.PortraitOrientation) {
678+ orientation = Qt.LandscapeOrientation;
679+ } else if (orientation == Qt.LandscapeOrientation) {
680+ orientation = Qt.InvertedPortraitOrientation;
681+ } else if (orientation == Qt.InvertedPortraitOrientation) {
682+ orientation = Qt.InvertedLandscapeOrientation;
683+ } else {
684+ orientation = Qt.PortraitOrientation;
685+ }
686+ sessionContainerLoader.item.orientation = orientation;
687+ }
688+ }
689 }
690 }
691
692@@ -263,5 +282,49 @@
693 // wait for animation to end
694 tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
695 }
696+
697+ function test_orientationPropagatedToChildren_data() {
698+ return [ { tag: "count=1", count: 1 },
699+ { tag: "count=4", count: 4 } ];
700+ }
701+
702+ /* Test orientation changes are propagated to all children immediately */
703+ function test_orientationPropagatedToChildren(data) {
704+ sessionCheckbox.checked = true;
705+ var rootSessionContainer = sessionContainerLoader.item;
706+ compare(rootSessionContainer.childSessions.count(), 0);
707+
708+ var i;
709+ var sessions = [];
710+ for (i = 0; i < data.count; i++) {
711+ var session = ApplicationTest.addChildSession(rootSessionContainer.session,
712+ "gallery");
713+ session.createSurface();
714+ rootSessionContainer.session.addChildSession(session);
715+
716+ // Check child SessionContainer has orientation matching the parent
717+ var delegate = findChild(rootSessionContainer, "childDelegate" + i);
718+ var childSessionContainer = findChild(delegate, "sessionContainer");
719+
720+ tryCompare(rootSessionContainer, "orientation", childSessionContainer.orientation);
721+
722+ sessions.push(session);
723+ }
724+
725+ // Change orientation and verify all children updated
726+ rootSessionContainer.orientation = Qt.LandscapeOrientation;
727+
728+ for (i = 0; i < data.count; i++) {
729+ var delegate = findChild(rootSessionContainer, "childDelegate" + i);
730+ var childSessionContainer = findChild(delegate, "sessionContainer");
731+
732+ tryCompare(rootSessionContainer, "orientation", childSessionContainer.orientation);
733+ }
734+
735+ // Clean up
736+ for (i = data.count-1; i >= 0; i--) {
737+ ApplicationTest.removeSession(sessions[i]);
738+ }
739+ }
740 }
741 }

Subscribers

People subscribed via source and target branches