Merge lp:~mzanetti/unity8/inputinfo-plugin into lp:unity8

Proposed by Michael Zanetti
Status: Superseded
Proposed branch: lp:~mzanetti/unity8/inputinfo-plugin
Merge into: lp:unity8
Diff against target: 7926 lines (+5307/-609)
87 files modified
CMakeLists.txt (+2/-0)
data/unity8-dash.desktop.in (+1/-0)
debian/changelog (+6/-0)
debian/control (+7/-4)
debian/unity8.install (+3/-0)
plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Unity/CMakeLists.txt (+1/-0)
plugins/Unity/InputInfo/CMakeLists.txt (+41/-0)
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp (+249/-0)
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux_p.h (+91/-0)
plugins/Unity/InputInfo/plugin.cpp (+32/-0)
plugins/Unity/InputInfo/plugin.h (+30/-0)
plugins/Unity/InputInfo/qdeclarativeinputdeviceinfo.cpp (+143/-0)
plugins/Unity/InputInfo/qdeclarativeinputdeviceinfo_p.h (+86/-0)
plugins/Unity/InputInfo/qinputinfo.cpp (+159/-0)
plugins/Unity/InputInfo/qinputinfo.h (+143/-0)
plugins/Unity/InputInfo/qmldir (+3/-0)
plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
plugins/Utils/CMakeLists.txt (+1/-0)
plugins/Utils/plugin.cpp (+4/-1)
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/DashApplication.qml (+3/-3)
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 (+185/-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 (+278/-0)
qml/Shell.qml (+112/-49)
qml/Stages/ApplicationWindow.qml (+53/-18)
qml/Stages/DesktopStage.qml (+23/-0)
qml/Stages/OrientationChangeAnimation.qml (+241/-0)
qml/Stages/PhoneStage.qml (+138/-40)
qml/Stages/SessionContainer.qml (+2/-8)
qml/Stages/SpreadDelegate.qml (+265/-24)
qml/Stages/SurfaceContainer.qml (+2/-2)
qml/Stages/TabletStage.qml (+150/-19)
qml/Stages/TransformedSpreadDelegate.qml (+50/-6)
qml/Stages/TransformedTabletSpreadDelegate.qml (+5/-2)
run.sh (+1/-1)
src/ApplicationArguments.cpp (+22/-0)
src/ApplicationArguments.h (+7/-11)
src/CMakeLists.txt (+6/-7)
src/Dash/CMakeLists.txt (+7/-1)
src/Dash/main.cpp (+12/-6)
src/UnityCommandLineParser.cpp (+5/-0)
src/UnityCommandLineParser.h (+2/-0)
src/main.cpp (+18/-8)
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 (+1151/-0)
tests/qmltests/tst_Shell.qml (+15/-69)
tests/qmltests/tst_ShellWithPin.qml (+6/-10)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+53/-1)
To merge this branch: bzr merge lp:~mzanetti/unity8/inputinfo-plugin
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Unity Team Pending
Review via email: mp+258241@code.launchpad.net

This proposal has been superseded by a proposal from 2015-05-18.

Commit message

Add a snapshot of Qt's WIP InputDeviceInfo API as a local plugin.

https://codereview.qt-project.org/#/c/101049/

This is supposed to be temporary and should be removed in favor of the upstream API when that one is ready/merged.

Description of the change

WIP: Just trying to get it through Jenkins once.

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
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:~mzanetti/unity8/inputinfo-plugin updated
1747. By Michael Zanetti

[ Albert Astals Cid ]
* Workarounds for concierge mode.
[ CI Train Bot ]
* New rebuild forced.
* Resync trunk.
[ Albert Astals Cid ]
* Make runtests fake a test error if make fails
* Make the test more stable
* Use dbus-test-runner instead of dbus-launch
[ Daniel d'Andrada ]
* DirectionalDragArea: improvements & API grooming (LP: #1417920)
* Fix EdgeDragEvaluator when a drag can happen both ways
  (Direction.Horizontal)
[ Josh Arenson ]
* Remove panelHeight property as it is unused.
[ Leo Arias ]
* Initial clean up of the autopilot tests set up. Removed the touch
  device from the test case. Moved the restart of unity to a fixture.
  Removed the unused DragMixin. Updated the setUpClass to use
  process_helpers. Removed the workaround for bug #1238417, already
  fixed. Use the toolkit helper to set the testability environment
  variable. Fixed the indicators base class that was restarting unity
  twice. (LP: #1238417, #1447206)
* Use the base class from the toolkit in autopilot tests.
[ Michael Zanetti ]
* emit application-stop when we're going down (LP: #1326513)
[ Michał Sawicz ]
* UNITY_SCOPES_LIST is no more
[ handsome_feng<email address hidden> ]
* When click the favorite scope in Dash Manager , it just return to
  the corresponding scope page. (LP: #1447056)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/inputinfo-plugin updated
1748. By Michael Zanetti

rebase on shellRotation

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/inputinfo-plugin updated
1749. By Michael Zanetti

merge trunk

1750. By Michael Zanetti

drop debug print

1751. By Michael Zanetti

merge trunk

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

Subscribers

People subscribed via source and target branches